How to Build a Client Portal Without Code (And Where It Quietly Breaks)

Build a client portal without code with separate logins and access levels

The short version: a branded login and a document list is the easy 60-70% any no-code tool ships in an afternoon - the dangerous 30-40% is row-level isolation so Client A never sees Client B, granular roles, SSO/SCIM, secure document handling, and audit logging.

RequirementWhat the easy/demo version doesWhat production actually needs
Data isolationOne list block queries the whole tableRow-level rules at the API so a query only returns the caller's rows
Field accessWhole record shown to the clientField-level permissions hiding internal notes within a shared record
RolesAdmin and user, two tiersA matrix of roles across records and fields
AuthenticationEmail and password loginSSO plus SCIM provisioning to deactivate departed users
DocumentsStatic list with shared linksSigned, expiring URLs and defensible e-sign
Audit trailNo record of accessAppend-only server-side log of who accessed or changed what
Verification"Looks right" in the editorNetwork-tab test confirms no other client's rows in the payload

A client portal is one of the most requested no-code builds, and the request always sounds the same: separate logins for each client, different access levels, a place to share documents and invoices so people stop emailing PDFs around.

You can get the first version live in an afternoon. A branded login screen, a list of documents, a profile page. Softr ships a portal template, Noloco ships one, Bubble has a dozen. None of that is the hard part.

The hard part is the sentence buried inside "different access levels": Client A must never, under any circumstances, see Client B's data. That single requirement is where most no-code portals quietly fail, and the failure is invisible until a client emails you asking why they can see someone else's invoice.


The 60-70%: Login, A Document List, A Branded Shell

Here is what comes easy, and it is genuinely useful work.

You connect a data source - Airtable, a Postgres table, Google Sheets - with a Clients table and a Documents table. You add a sign-up or invite flow so each client gets their own login. You drop in a list block that shows documents, an upload block, maybe a status field so clients can see where a project stands. You style it with your logo and brand colors so it does not look like a spreadsheet.

This is a real portal. For a freelancer with five clients who already trust you, it might be all you need. The tools are good at this layer, and you should not over-engineer past it if your stakes are low.

The trouble starts the moment you have two clients and one shared list block. We covered the same pattern from the team-facing side in building an internal tool without code - the easy 60-70% is always the screens, and the access model is the part that does not show up in a demo.


The Nightmare: A List Block That Shows Everyone Everything

Picture the leak. You build one "Invoices" page. It is a list connected to your Invoices table. In the editor it looks perfect because you, the builder, can see all the rows. You ship it. Client A logs in and sees their invoices - and Client B's, and Client C's, sitting right there in the same list.

This happens because a list block, by default, queries the whole table. Visibility is a property of the data layer, not of the screen. If you do not scope the query to "rows where this row belongs to the logged-in user," the screen shows everything the underlying source returns.

The dangerous version of this is the one that looks fixed but is not. Some builders let you hide rows with a front-end filter - the block only displays records matching the current user. The records still travel to the browser; they are just hidden by CSS or client-side logic. Anyone who opens the network tab, or hits the underlying API, sees the full table. Noloco is explicit about this distinction in its own docs: front-end filters are not security, and record-level permissions enforce access at the API level instead. That is the line that matters.


Row-Level Scoping Is The Whole Game

The correct fix is to enforce, at the data layer, that a query can only ever return rows the requester owns.

In Softr this is done with global data restrictions: you define record-level rules once - "users can only view records where the Client field equals their account" - and they apply across every block in the app rather than per screen. Softr's user groups and permissions guide walks through how view, create, edit, and delete restrictions stack, and notes that the stricter create/edit/delete restrictions sit behind the business plan.

If you have your own database, the gold standard is Postgres Row Level Security. RLS attaches a policy to the table itself so that every query - no matter where it comes from - behaves as if it had a WHERE clause restricting it to the caller's rows. Supabase enables it by default on Table Editor tables and documents the policy patterns in its Row Level Security guide. The important property: the restriction lives below the API, so a leaky front end or a hand-crafted request cannot route around it.

The test is simple and you should run it on every portal you build: log in as Client B, open the browser network tab, and inspect the actual API responses - not the rendered page. If Client A's rows show up in the raw payload, you have a leak, even if the screen looks clean. This is the same class of problem we cover in no-code security risks: the demo looks secure because the builder sees everything by design.


Roles Are Not Just Admin And User

Real portals are not two-tier. A service firm portal usually needs: the client contact who sees their own projects, the client's finance person who sees invoices but not project files, your internal account manager who sees several clients, and your admin who sees everything. That is four distinct permission shapes, and they cut across two axes at once - which records, and which fields within a record.

The field axis is the one people forget. A client should see a project's status and deliverables but not your internal margin notes sitting in the same record. That needs field-level permissions, not just row-level ones. Noloco separates these explicitly with field-level permissions for create/read/update per field, and Postgres offers the equivalent with column-level security.

Design this as a matrix before you build screens. Roles down the side, tables and sensitive fields across the top, and a clear allow/deny in every cell. If you add permissions screen by screen instead, you will end up with a portal where the rules are scattered across forty blocks and nobody can answer "who can see invoices" without clicking through all of them.


SSO, Documents, And The Audit Trail

Three things separate a hobby portal from one a real business will sign off on.

SSO and provisioning. Smaller clients are fine with email-and-password. Larger ones will ask you to support their identity provider - log in with Okta, with Google Workspace, with their corporate SSO - and to deactivate a user automatically when that person leaves their company. That second part is SCIM provisioning, and it is distinct from login. WorkOS has a clear explainer on how SCIM and SSO differ and work together: SSO handles the authentication, SCIM keeps the user list in sync so a departed employee loses access without you touching anything. Most no-code builders support basic SSO; few handle SCIM, and that gap is often what blocks an enterprise deal.

Secure documents and e-sign. A document list is easy. A secure document list is not. Files need signed, expiring URLs so a link shared in one client's portal cannot be replayed to fetch another client's file. If you store files in Supabase Storage, access is governed by the same policy engine as your tables - its storage access control docs show how to gate downloads with RLS-style rules. E-signature usually means embedding a real provider rather than rolling your own, because a signature you cannot prove in a dispute is worthless.

Audit logging. When a client asks "who downloaded this contract and when," you need an answer. That means an append-only log of who accessed or changed what, recorded server-side where a user cannot edit it. Most no-code portals have no audit trail at all, and you discover this exactly when you need it most - during a dispute or a security review.


What To Decide Before You Build

The order of operations matters more than the tool. Before you place a single block:

  1. Write the permission matrix. Roles by records by fields. If you cannot fill in every cell, you do not yet understand the requirement.
  2. Decide where access is enforced - data layer, not screen. Confirm your tool does record-level rules at the API, and verify it with the network-tab test.
  3. Know your ceiling. If you need SCIM, hardware-key SSO, signed audit logs, or e-sign you can defend in a dispute, find out now whether the builder supports it or whether you will hit a wall at the exact moment a serious client signs.

A client portal is a multi-tenant access-control problem wearing a friendly document-sharing costume. The screens are the easy 60-70%. The row-level scoping, the role matrix, the SSO, and the audit trail are the 30-40% that decides whether the portal is something you can put a real client's name on - or a leak waiting for the wrong login.

If you have built the easy version and hit that wall, the honest options are to learn the permission model deeply, hire it out, or use a platform like Creatr's DeepBuild that treats access control as a first-class part of the data model rather than a setting you bolt on per screen. Whatever you choose, decide it before a client sees the wrong invoice - not after.

Common questions

Why can clients see each other's data in a no-code portal?
Because a list block queries the whole table by default and shows every row the data source returns. If you only hide other clients' records with a front-end filter, the data still reaches the browser. You must scope access at the data layer with record-level rules so a query can only ever return rows the logged-in user owns.
What is row-level security and why does a client portal need it?
Row-level security enforces, at the database, that every query behaves as if it had a WHERE clause restricting it to the caller's own rows. A client portal needs it because visibility is a property of the data, not the screen. Without it, a hidden front-end filter can be bypassed through the network tab or the raw API.
Can no-code tools handle enterprise SSO for a client portal?
Most support basic SSO like logging in with Google or Okta. Few handle SCIM provisioning, which automatically deactivates a user when they leave the client's company. That gap often blocks enterprise deals, so confirm whether your builder supports SCIM before promising it to a large client.
How do I test that my client portal isn't leaking data?
Log in as a second client, open the browser network tab, and inspect the raw API responses rather than the rendered page. If another client's rows appear in the payload, you have a leak even when the screen looks clean. Run this test on every portal before sharing it with real clients.
Niraj Kumar Jha
Niraj Kumar Jha
Full Stack Engineer
Updated

Full Stack Engineer at Creatr, building DeepBuild - the system that ships production web apps in 24 hours. Niraj works across the entire stack, from database architecture to frontend delivery, and has a sharp focus on shipping things that actually work in production.

Have something serious on the calendar?
Let's ship it this week.

Book a call