How to Create a Client Portal That Keeps Every Account Separate

Playcode Team
18 min read
#client portal #customer portal #AI app builder

A client portal is not just a login in front of a shared folder. It needs a client or tenant identity, memberships, project-scoped records, server-side authorization, understandable document and request states, and an operator path for correcting access without exposing another customer.

This guide builds the smallest useful version first: two fictional client accounts, one private project, document metadata, one request lifecycle, and list, detail, update, and download checks. Storage, email, and payments stay explicit optional provider paths rather than invisible promises inside the core portal.

Illustrative Acme Studio portal with a private project, document statuses, client account, and open request
Illustrative concept with fictional project and approval states, not a Playcode product screenshot or workflow proof. The actual result depends on your client model, project workflow, access rules, and design brief.

QUICK ANSWER

How do you create a client portal?

Define the client account, memberships, project records, document metadata, requests, and allowed state changes before building screens. Enforce client scope on every server-side list, detail, update, download, and export path. Test signed out and across two accounts, publish over HTTPS, then monitor and recover with bounded audit events that exclude private content.

Prepare two clients before you design the portal

Use fictional organizations, people, files, and project descriptions. Two client accounts are the minimum useful fixture because tenant-isolation defects stay invisible when every test runs as the owner or one administrator.

  • A client and user relationship map: Decide whether one person may belong to several client accounts, whether each client can have several projects, and which operator roles may invite, revoke, view, update, download, or export records.
  • One project and request lifecycle: Write the first project states and one request path, such as open, waiting for client, in review, resolved, and reopened. Name who may trigger each transition and what a stale concurrent update should do.
  • A document metadata and storage contract: Separate filename, owner, tenant, version, status, MIME type, byte size, storage reference, and retention from the actual file bytes. Choose app storage within current plan limits or a named external provider path only after checking its requirements.
  • A privacy, retention, export, and support owner: Name who can investigate access and correct records, which personal fields are necessary, when invitations, audit events, documents, and inactive accounts are deleted, and how a client requests export or deletion where applicable.

Credentials and access

CredentialMinimum accessStorage and rotation
Optional file-provider API credential
Server-side API key, OAuth client, or service credential from the storage provider you choose
Only the container, folder, object, upload, and download operations required by the portal; avoid broad account administration by defaultServer-side secret configuration only, never browser code, source control, generated UI, URLs, document metadata, or routine logs Create or authorize a replacement, update the server configuration, verify upload and authorized download, then revoke the previous credential
Optional notification-provider credential
Email API key or SMTP/API credentials from the provider you select
Send only from the verified identity and environment required by portal notificationsServer-side secret configuration, separated by environment and never returned to the client browser Issue a replacement, verify one fictional notification, monitor errors, then revoke the old key according to the provider procedure

Choose how client scope reaches every record

Tenant isolation becomes easier to reason about when every private record carries one authoritative client scope and every request derives its allowed scope from the current server session. The choice affects query safety, shared collaborators, and operator tooling.

ApproachBest forTradeoff
Direct client ID on each private recordSmall portals where every project, document, and request belongs to exactly one client account.It is explicit and simple to query, but shared records or cross-client work need a separate deliberate relationship rather than a nullable client field.
Project membership plus inherited client scopePortals where access differs by project and one client account may run several teams or engagements.It supports precise project access, but every record query must prove the project and client relationship and the operator view must explain why access exists.
Workspace role plus project exceptionsMature portals with a common client-wide view and a small number of restricted or cross-functional projects.It is flexible, but role, grant, denial, expiry, and revocation precedence can become difficult to audit and test.

Recommended:Start with a direct client ID and an explicit project membership when the first portal has one or two projects. Add client-wide roles only when a real user needs the same access across several projects. Never use a browser-selected client ID as the authorization source of truth.

Create a client portal step by step

Define tenant-scoped records, build invitations and project states, protect server routes, separate document metadata from file storage, test account isolation, publish, and operate the portal.

STEP 01

Write the first complete client journey

Bound the release around one private project and one client-to-operator request lifecycle.

Describe invitation issue, acceptance, sign-in, project list, project detail, document state, client request, operator response, session expiry, revocation, and record export in order.

For every view, name loading, signed-out, empty, denied, stale, save-error, and recovery states. A portal that only shows populated owner data is not ready to define its authorization boundary.

Expected result: The brief has an observable outcome for an invited client member, authorized client member, wrong client account, signed-out visitor, operator, expired session, and revoked membership.

Verify it: Walk both fictional clients through the same project URL. If the expected data and server result are not explicit for either account, update the brief before building.

STEP 02

Model tenant, membership, project, document, request, and audit records

Keep identity, client membership, workflow state, and file metadata as separate facts.

Give every private record a stable ID, authoritative client or project scope, status, created and updated timestamps, and version where concurrent updates matter. Store the signed-in user separately from their membership in each client account.

Document metadata should describe the authorized record and storage reference, not pretend the database row is the file vault. An audit event should record bounded IDs and the allowed transition, not copy the private document or request body.

client-portal.model.ts
type ClientMembership = {
  id: string
  clientId: string
  userId: string
  role: 'client' | 'operator'
  status: 'invited' | 'active' | 'revoked'
  createdAt: string
  updatedAt: string
}

type ProjectRequest = {
  id: string
  clientId: string
  projectId: string
  createdByUserId: string
  status: 'open' | 'waiting-client' | 'in-review' | 'resolved'
  version: number
  createdAt: string
  updatedAt: string
}

type DocumentRecord = {
  id: string
  clientId: string
  projectId: string
  storageRef: string
  filename: string
  mimeType: string
  sizeBytes: number
  status: 'uploading' | 'ready' | 'rejected' | 'deleted'
}

Expected result: Revoking one membership does not delete the client, project, request history, document metadata, or another user's membership.

Verify it: Seed two clients with one project each, give one fictional user access to the first only, and confirm every private record can be traced to exactly one authorized tenant path.

STEP 03

Build retry-safe invitations and sessions

Make invitation acceptance single-purpose, time-bounded, revocable, and safe to repeat.

Issue an unguessable invitation token bound to the intended client, role, expiry, and one invitation record. Store only the safe server-side representation needed for verification and never place the raw token in logs or generated images.

On acceptance, validate expiry and revocation, create or reuse the user relationship, and create the client membership once. Define session duration and what happens to an open page when the session expires or the membership is revoked.

Expected result: One invitation produces at most one intended membership, while expired or revoked invitations and sessions cannot restore access.

Verify it: Accept the same fictional invitation twice, retry after interrupting the response, revoke the membership, and repeat a protected request from the old session.

STEP 04

Enforce client scope on every server handler

Authorize collections, details, updates, downloads, and exports independently.

Resolve the current identity from the server session. Derive active client and project access from server records, then verify the requested record belongs to that tenant before reading or changing it. Do not trust a client ID, role, or hidden menu supplied by the browser.

Apply the same rule to project lists, detail pages, comments, request updates, document metadata, file downloads, exports, and operator actions. Return a denial without private fields when the check fails.

Expected result: A known project, request, or document ID remains unreadable and unchangeable to a signed-out visitor or the other ordinary client account.

Verify it: Copy authorized list, detail, update, download, and export requests into a private session and the second account, then inspect raw responses as well as visible pages.

STEP 05

Separate document metadata from file storage

Validate the upload and authorize the storage outcome before marking a document ready.

Validate allowed MIME type, maximum bytes, filename handling, client and project scope, ownership, retention, and storage status on the server. Reject the upload before creating a usable record when the input is invalid.

For app storage, follow current plan limits and protect each download request. For an external provider, configure its account, plan, server-side credential, API path, rate limits, and error behavior. Do not expose a provider key or permanent public object URL.

Expected result: Each ready document maps to one stored object and authorized client record, while failed uploads stay rejected or removable rather than appearing ready.

Verify it: Try an allowed small fixture, a disallowed type, an oversized fixture, an interrupted upload, and a download from the other client account. Compare metadata, storage references, and server decisions.

STEP 06

Implement request transitions and notification failure states

Commit the client request before asking an external provider to deliver a message.

Allow only named request transitions and use a version check for concurrent changes. Append a bounded audit event with actor ID, record ID, previous and next status, timestamp, and reason code.

After the validated request is durably saved, send or queue a notification. Track pending, sent, and failed delivery separately. An email provider requires its own account, plan, server-side credential, sender verification, limits, and operational review.

Expected result: One client action creates one request even when notification delivery fails, and an operator can retry only the provider step.

Verify it: Submit the same request twice with one idempotency key, make two concurrent status changes, simulate notification failure, then compare request count, final version, audit events, and delivery state.

STEP 07

Test isolation, invalid uploads, retries, and expiry

Exercise the cases that an operator-only demo cannot reveal.

Test signed out, private session, invited but unaccepted, authorized client A, ordinary client B, operator, expired session, and revoked membership. Repeat direct list, detail, update, download, and export requests rather than only clicking visible navigation.

Test invite reuse, interrupted acceptance, duplicate request submission, concurrent update conflict, invalid upload type and size, interrupted storage, failed notification after save, and a privacy sweep of logs and generated assets.

Expected result: Unauthorized paths remain denied, retries stay single, stale updates are detected, invalid files create no usable record, and provider failure does not lose the client request.

Verify it: Record membership, project, document, and request counts before and after each case, then inspect server responses and final records rather than trusting the success message.

STEP 08

Publish and run a production tenant-isolation smoke test

Verify the final HTTPS environment with both fictional client accounts before inviting real customers.

Publish the portal, accept one clearly labeled fictional invitation in a private browser, open client A's project, submit one request, review one document record, and resolve the request from the operator view.

Open the known project and document paths as client B and signed out, expire and revoke client A's session, inspect bounded logs for private payloads, and remove or archive the fixtures under the retention policy.

Expected result: Client A completes the published workflow once, client B remains isolated, and every protected path is denied after expiry and revocation.

Verify it: Save the fictional membership, project, document, request, and server-request IDs, repeat each protected call from the wrong contexts, and inspect the final stored records.

STEP 09

Monitor with safe IDs and rehearse recovery

Collect enough context to diagnose the workflow without turning logs into another copy of the portal.

Monitor invitation outcomes, authentication failures, denied actions by reason, upload state, request transitions, stale updates, notification delivery, exports, and operator corrections using bounded IDs and reason codes.

Provide operator actions to replace an invite, revoke access, correct a request with an audit reason, clean an incomplete upload, retry a notification, and export client records. Treat record repair, source-code export, client-data export, file handoff, and whole-app restore as separate decisions.

Expected result: An authorized operator can explain and correct a fictional portal failure without reading secrets, invite URLs, document contents, or full personal payloads from logs.

Verify it: Rehearse one accidental revoke, incomplete upload, stale request update, notification outage, record export, and restore-versus-repair decision, then document the exact IDs and action used for each.

Minimum client-portal test matrix

Use stable fictional accounts and inspect stored records after each action. A page that looks denied can still return private data in its network response, and a visible error can hide a successful duplicate write.

TestScenarioExpected result
happy pathInvite client A, accept once, open its project, review one document record, submit one request, and resolve it as the operator.One active membership and one request exist, client A sees only its records, and the audit trail shows the allowed transitions.
invalid inputRequest client A's project and document as client B and signed out, then submit a missing field, disallowed file type, and oversized fixture.Every unauthorized server response is denied, no other-client data is returned, and invalid uploads create no usable document record.
retryAccept the same invite twice, repeat the same request attempt, interrupt one response, and retry a failed notification after save.Membership and request counts remain single, and only notification delivery is retried.
production smokeComplete the fictional workflow on the published HTTPS portal, then repeat protected calls from client B, a private session, an expired session, and after revocation.The intended workflow works once and every wrong or expired context remains isolated in the final environment.

Client-portal failures and exact checks

Diagnose identity, membership, tenant scope, record version, storage state, and notification state before changing the interface or asking the client to retry.

SymptomLikely causeCheckFix
Client B can see client A's project or documentThe handler checked sign-in or record ID but did not verify active client membership, tenant scope, and the requested action.Replay the exact list, detail, update, download, and export calls from client B and inspect the raw response and authorization context.Require identity, membership, tenant, record, and action authorization together on every protected server path.
A document says ready but the file is missingMetadata was committed before storage succeeded, or an interrupted upload left an unusable reference in the ready state.Compare the document ID, upload status, storage reference, byte count, and bounded app-storage or provider result.Use explicit uploading, ready, rejected, and deleted states, and mark ready only after the stored object is confirmed.
A client resubmits because the notification never arrivedRequest persistence and notification delivery were treated as one success boundary.Look up the stable request ID and separate notification status before repeating the client write.Return the saved request independently and retry only the provider notification.
A revoked member can still download a documentThe portal trusted cached browser state or a long-lived file URL without current authorization.Repeat the download request directly from the existing session immediately after revocation.Authorize each download at request time and keep any temporary URL short-lived, record-scoped, and revocable.
Two operators overwrite each other's request updateThe update accepted stale state without an expected version or documented conflict rule.Compare submitted and stored versions, timestamps, actors, and audit events for both updates.Require an expected version, reject or reconcile stale writes, and show a recoverable conflict state.

Deploy and operate the client lifecycle

Deploy

Publish over HTTPS and verify the final domain, invite acceptance, session cookie behavior, project list and detail, document record and authorized download, request transition, operator route, expiry, revocation, and export with fictional accounts.

If external storage, email, or payments are added, configure separate development and production accounts or credentials on the server. Re-check the exact endpoint, sender, container, webhook, limits, and provider environment before enabling real client data.

Monitor

Track invitation outcome, authentication failure, denied action by reason, document upload state, request transition, stale update, notification delivery, export, and operator correction with stable bounded IDs.

Do not log passwords, session cookies, invitation tokens, credentials, document contents, permanent file URLs, private request descriptions, or full client profiles.

Recover

Support replacing an invitation, revoking membership and sessions, correcting a request with an audit reason, cleaning an incomplete upload, retrying a notification, and exporting a client's authorized record set.

Document record repair, code export, client-data export, file handoff, and whole-app restore separately. Restoring a whole-app point can also return the database and file state to that point, so compare it with record-level repair before acting.

Client-portal security and privacy checklist

Tenant isolation belongs at the server boundary. Collect the least personal and document data the service needs, keep every long-lived access path revocable, and make operator corrections auditable.

  • Authorize every list, detail, update, upload, download, export, and operator action against current identity, active client membership, tenant scope, record, and requested action on the server.
  • Use time-limited, unguessable, revocable invitations and server-managed sessions. Never store raw passwords, invite tokens, session cookies, or provider credentials in source, browser code, screenshots, URLs beyond the required invite path, or routine logs.
  • Validate file type, byte size, filename handling, storage outcome, tenant scope, retention, and download authorization on the server. Do not treat a provider object URL as permanent permission.
  • Separate a durable request save from notification delivery and optional payment events. Verify provider signatures where applicable, process retry identifiers once, and provide reconciliation rather than duplicating the client record.
  • Minimize personal fields, bound text and file inputs, name a retention owner, and define client export, correction, deletion, and legal-review requirements before collecting sensitive or regulated content.
  • Record stable IDs, transitions, versions, timestamps, and reason codes in the audit trail while excluding private descriptions, document bytes, secrets, and unnecessary personal payloads.

Questions about creating a client portal

What is the minimum viable client portal?

Two fictional client accounts, one private project, one client membership and invitation path, one document record, one request lifecycle, and a protected operator view. Test list, detail, update, download, expiry, and revocation before adding integrations or real client data.

Do I need a file-storage provider?

Not necessarily. The portal may use Playcode persistent file storage within current plan limits or a configured external provider. Choose from real size, retention, regional, access, and migration requirements, and keep metadata plus authorization explicit either way.

How do I prevent one client seeing another client's data?

Derive the current identity and client membership on the server, verify the requested record belongs to that tenant, and authorize the action before reading or changing data. Repeat the exact request signed out and from a second ordinary client account.

Should a client ID from the browser select the tenant?

It may help the interface choose context, but it must not grant access. The server should derive allowed clients from the authenticated session and membership records, then reject any requested client or project outside that set.

How should client document uploads work?

Validate MIME type, size, filename handling, tenant and project scope, storage result, and retention on the server. Keep incomplete uploads out of the ready state and authorize every download when requested.

Can the portal send email or payment notifications?

Yes through a provider path when you supply the account, plan, server credentials, and requirements. This guide does not claim native email or payment services or a reproduced provider integration. Keep the core portal record independent from provider delivery.

What belongs in a client-portal audit trail?

Use stable actor, client, project, request, and document IDs; previous and next state; record version; timestamp; and a bounded reason code. Do not copy credentials, invitation URLs, document contents, private descriptions, or full personal payloads into routine logs.

Can I export the portal, client records, and files?

The source code can be exported. Treat code export, database-record export, authorized file handoff, and whole-app recovery as separate contracts. Verify each exact path before relying on export as a complete migration or backup.

Build the smallest complete client lifecycle

Turn Your Client Workflow Into a Working Portal

Describe the client accounts, project records, document states, requests, server access rules, and operator recovery path. Playcode can build the first version and help you test each account boundary.

Start Building with Playcode

No native file vault, email, or payment service is implied. External providers require their own accounts, credentials, and setup.

Have thoughts on this post?

We'd love to hear from you! Chat with us or send us an email.