How to

How to build a B2B customer portal

Customers email an account manager to ask for an invoice, a usage figure, or who has access. Here is the model behind a portal that answers those, the prompts to build it, and what it takes to run it.

The short answer

A B2B customer portal is four parts: an organisation model where users belong to a company, a tenant boundary enforced in one place, self-service for what customers currently email about, and customer-side admin roles. The one decision that cannot be retrofitted cheaply is the tenant boundary. Every query must be scoped by organisation from the first commit, because adding that later means auditing every query you have written.

portal.helix-app.ai

Customer portal

288 of 300

Seats in use

4

Open requests

14 Nov

Renewal date

2

Invitations pending

Halden Foods

What this organisation sees when they sign in

Plan
Pro, annual
Contract
1 Dec 2025 to 14 Nov 2026
Seats
288 of 300
Account manager
R. Iyer
Last invoice
34,166, paid

Their own recent activity

  • Today s.roche@halden.example invited by their administrator, not by us
  • 2 days ago Invoice INV-20418 downloaded
  • Last week Support request opened visible to them with its status
  • Last week Seat removed for a leaver by their administrator
  • Never Anything belonging to another organisation the boundary this whole build rests on
A sketch of what one customer sees. Everything here belongs to their organisation and nothing else can reach it, which is the property every query in the app has to enforce.

What matters here

  • A customer portal is four parts: an organisation model, an enforced tenant boundary, self-service answers, and customer-side roles.
  • Scope every query by organisation from the first commit. Retrofitting a tenant boundary means auditing every query you wrote.
  • Users belong to organisations, and some belong to several. Model that on day one rather than assuming one company per user.
  • Give the customer an administrator role. Managing their own users removes a large share of the requests a portal is meant to eliminate.
  • Start from what customers actually email about: invoices, usage, who has access, and the status of a request.
  • Staff need a view-as capability that is read-only, time limited, visible and audited.

Who this is for

You are building or specifying a portal your customers will sign into. What you want is the multi-tenant structure right at the start, and the self-service scope aimed at the questions your team currently answers by hand.

How it works in practice

What a customer does when they sign in, and what has to be true underneath for that to be safe.

  1. 1

    Users belong to an organisation, and sometimes to several

    Partners, contractors and acquisitions all produce that case, and a single organisation column on the user record is expensive to unpick later.

  2. 2

    Every query is scoped by organisation, in one place

    Denying by default, so a query with no explicit scope returns nothing rather than everything.

  3. 3

    Boundary tests are written before any feature

    This is the one build where they are not optional.

  4. 4

    Customers answer their own questions

    Invoices, usage against entitlement, who has access, documents, and open requests. In most B2B businesses that is what your team answers by email.

  5. 5

    Their administrator manages their own users

    Inviting, removing and setting roles, which removes a large share of the inbound the portal exists to reduce.

  6. 6

    Staff help without breaking the boundary

    A read-only, time-limited, visibly indicated and audited view-as, using a separately authenticated staff role.

What a customer portal is made of

A customer portal is not a logged-in version of your website. It is four parts, and the second is the one you cannot add later cheaply.

An organisation model

Organisations as first-class records, users belonging to them through a membership with a role, and support for a user belonging to more than one. Partners, contractors and acquisitions all produce that case.

An enforced tenant boundary

Every query scoped by organisation in a single layer, denying by default. This is the security property the whole portal rests on and it is not something to reimplement per page.

Self-service answers

The things customers currently email about: invoices and payment history, usage against entitlement, who at their company has access, documents, and the status of open requests.

Customer-side roles

An administrator at the customer who can invite and remove their own users, set roles, and see the audit of who did what. This removes a large share of the inbound the portal exists to reduce.

These prompts start from a Helix project, which is what handles sign-in, credentials and hosting. Start with Helix

The prompts

Paste these into Claude Code, Codex or Cursor in order. Each one leaves you with something that runs, so you can stop after any step.

  1. The tenant boundary, before any feature

    The one decision here that cannot be retrofitted cheaply.

    In this Helix project, build me a B2B customer portal. Before any feature,
    get the tenant model right, because this is the part that cannot be added
    later without auditing everything.
    
    Organisations are first-class records: name, status, plan, account
    manager, created date, and a reference to our CRM or billing record.
    
    Users have no organisation column. Instead there is a membership record
    linking a user to an organisation with a role, a status, who invited them
    and when. A user can belong to more than one organisation. Partners,
    contractors and customers who acquire each other all produce that case.
    
    Add an active organisation in the session, with a switcher for people who
    belong to several.
    
    Then build one data access layer that every query goes through. It
    requires an organisation id and denies by default: a query with no explicit
    scope returns nothing, not everything.
    
    Now write tests for that boundary before writing a single feature. For
    every table, assert that a user in organisation A cannot read, update or
    delete a record belonging to organisation B, whether by id, by search, or
    through any list endpoint.
    
    I sign in through Helix, so do not build a login page. Read the signed-in
    user from the runtime. And do not put any key or connection string in the
    code: if this needs to reach another system, tell me and I will create the
    connection in my workspace.

    Worth knowing. Boundary tests written first are the cheapest insurance in this build. Written afterwards they are an audit of everything you already shipped.

  2. Build for the emails your team actually gets

    In the order those emails arrive.

    Now the customer-facing features, in this order, because it is roughly the
    order your team currently answers them by hand.
    
    Account overview: plan, contract dates, renewal date, primary contact, and
    the account manager with a way to reach them.
    
    Billing: invoices with status and amount, downloadable, plus payment
    history. This is the single most requested item in most portals.
    
    Usage against entitlement: what they have used this period against what
    they are entitled to, with history. Put the definition of the metric next
    to it. A usage number people do not understand generates more enquiries
    than it prevents.
    
    Users: everyone at their organisation with access, their role, and their
    last sign-in.
    
    Documents: contracts, order forms, security documentation, anything we
    currently email on request.
    
    Requests: any open support ticket or service request with its status.
    
    For each of these, if we do not have a reliable source for the data, say
    so rather than showing an approximation. A wrong invoice figure costs more
    trust than a missing feature.
  3. Let the customer do the work you are doing for them

    Customer-side administration, carefully.

    Define roles within an organisation in a config file: at minimum
    administrator, member and billing contact, each with a description of what
    it can see and do.
    
    An administrator at the customer can invite users by email, set and change
    roles, deactivate people, and see pending invitations. Taking the account
    manager out of that loop removes a large share of the inbound this portal
    exists to reduce.
    
    Invitations expire, and re-inviting issues a new one rather than extending
    the old.
    
    Give their administrator an organisation-level audit: who signed in, who
    was invited or removed, who changed a role, who downloaded which document.
    
    Support optional domain-based joining, off by default, where anyone with
    an email at a verified domain can request access and an administrator
    approves. Verify the domain properly and never enable it for a free-mail
    domain.
    
    And give users notification preferences, because a portal that emails too
    much gets filtered and then nobody sees the important one.

    Worth knowing. Domain-based joining is convenient and dangerous. Off by default, verified properly, never for a free-mail domain.

  4. Helping a customer without breaking the boundary

    Staff access, and the shortcut to never take.

    Add a staff role that exists outside the organisation model and is
    authenticated separately from customer users.
    
    Never grant a staff member a membership in a customer organisation to give
    them access. It works immediately, and it destroys the meaning of both the
    boundary and the audit log.
    
    Instead build view-as for support: strictly read-only, time limited,
    clearly indicated in the interface throughout, and audited on entry and
    exit with the reason recorded.
    
    Give staff a view of any organisation showing the same data the customer
    sees, so a support call is about the same screen.
    
    Add health signals for the account team: last sign-in by anyone at the
    organisation, active users against licensed, and features never used. An
    organisation where nobody has signed in for sixty days is worth knowing
    about before the renewal.
    
    And rate limit every customer-facing endpoint. This is an internet-facing
    app holding one customer's data next to another's.
  5. Connect it to real systems

    The step that turns a working prototype into something with your data in it.

    Now connect it to the systems we actually run, so it works on real data
    rather than the rows you seeded.
    
    Anything in angle brackets is a placeholder. Swap it for whatever we use
    instead, and have the app name the connection it wants rather than assume
    a vendor, so changing my mind later is a config change and not a rewrite.
    
    <Stripe>. Read invoices, payment history and plan, which is the single
    most requested thing in any portal.
    
    <Okta>. Authenticate customer users and staff separately, and support a
    customer bringing their own.
    
    <Amplitude>. Show usage against entitlement, with the metric definition
    beside it.
    
    <Zendesk>. Show the status of their open requests, so they stop emailing
    to ask.
    
    <Salesforce>. Read the account, contract dates and account manager, so the
    portal shows the same thing your team sees.
    
    <Google Drive>. Serve contracts, order forms and security documentation
    that you currently email on request.
    
    <Snowflake>. Read the usage totals behind the entitlement view, aggregated
    once a day rather than counted live on every page load.
    
    Only write where I have said to write. Everything else is read only.
    
    Do not write a key, a token or a connection string anywhere in the code,
    and do not ask me to paste one. Tell me which connections you need and I
    will create each as an authentication in my Helix workspace. Reference
    them by alias.

    Worth knowing. Every name in angle brackets is a placeholder for whatever you run. Helix holds the credential as a workspace authentication, so the app references an alias and never the secret itself.

  6. Ship it

    The last step of every build: a URL, and the right people on it.

    Deploy my app.

    Worth knowing. Your assistant runs helix deploy underneath and the app comes back as a URL. This one is internet facing with customer and staff users, so set Access Control before sharing, and expect enterprise customers to ask to bring their own identity provider.

What it connects to

Everything a customer sees here is assembled from systems they have no access to, which is most of the work.

  • Stripe

    Read invoices, payment history and plan, which is the single most requested thing in any portal.

  • Okta

    Authenticate customer users and staff separately, and support a customer bringing their own.

  • Amplitude

    Show usage against entitlement, with the metric definition beside it.

  • Zendesk

    Show the status of their open requests, so they stop emailing to ask.

  • Salesforce

    Read the account, contract dates and account manager, so the portal shows the same thing your team sees.

  • Google Drive

    Serve contracts, order forms and security documentation that you currently email on request.

  • Snowflake

    Read the usage totals behind the entitlement view, aggregated once a day rather than counted live on every page load.

Each line says whether the app reads, writes or both. Anything not described as writing should be read only.

Named systems are the ones most teams actually run, not a list of the only ones that work. Swap in whatever you use.

Each connection is an authentication in your Helix workspace, referenced by alias. The app names the connection it wants and never holds the credential, so nothing here ends up in your code and any of it can be rotated or revoked without a redeploy.

Running it for real

The build gets you a working portal. It is internet facing, it holds one customer's data next to another's, and it will be reviewed by your customers' own security teams.

AI Deployment

Get it to a URL people can open

One command takes the app from your assistant to a live address, so the people who need it get a link rather than instructions for running it locally.

App Security

Put your identity provider in front of it

Customer users and staff need separate authentication paths, and enterprise customers will ask to bring their own identity provider. Handling sign-in outside your application code is what makes that a configuration rather than a project.

App Security

Connect the system of record with managed credentials

Reading billing, usage and contract data from your internal systems needs real access. Granting it to the app rather than embedding keys means credentials can be rotated or revoked without a redeploy.

App Registry

Put a name on it

Every app carries a named owner and an entry IT can see, so the tool does not become nobody's problem when the person who built it changes role.

Cost Management

Know what it costs to run

Per-app spend visibility, with budgets and caps, so an internal tool cannot quietly become a line item nobody can explain.

Questions people ask

What is a B2B customer portal?

A signed-in area where customers see their own account: invoices, usage against entitlement, who at their company has access, documents, and open requests. It differs from a consumer login in that users belong to an organisation rather than existing alone.

What is the most important decision when building one?

Enforcing the tenant boundary in a single data access layer that denies by default, from the first commit. Adding it later means auditing every query already written, and a single missed one exposes one customer's data to another.

Should a user be able to belong to more than one organisation?

Yes, and model it from day one. Partners, contractors, consultants and customers who acquire each other all produce that case, and a single organisation column on the user record is expensive to unpick once there is data.

What should a customer portal offer first?

Whatever your team currently answers by email. In most B2B businesses that is invoices, usage figures, who has access, and the status of open requests, roughly in that order.

How should support staff access a customer account?

Through a view-as capability that is read-only, time limited, visibly indicated and audited, using a staff role authenticated separately. Adding a staff member to a customer organisation works immediately and destroys the meaning of both the boundary and the audit log.

Can I build this without an engineering team?

The prompts here are written for Claude Code, Codex or Cursor and will get you a working portal. This is the one build on this site where the boundary tests are not optional, so write them first and read the results yourself.

Last reviewed September 2026.

Build it, then run it properly

Helix is the governed runtime for AI-built apps. Deploy what you build, put SSO in front of it, connect it to your systems with managed credentials, and give it a named owner.