# An agency with several clients

One client, one workspace. The workspace is the isolation root: nearly every record carries a workspace id and is filtered automatically, and the filtering happens three separate times before any data reaches a screen. That makes client separation structural rather than a matter of care — and it also means there is no view across clients.

- **Status:** Available
- **Audience:** both
- **Channels:** email, phone, whatsapp
- **In the app:** #/account, #/mailboxes, #/billing
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/use-cases/agency-multiple-clients/

## What a workspace boundary actually enforces

Isolation is enforced three times on the same request: an allowlist in the middleware refuses any path a customer session is not permitted to call, the SQLAlchemy workspace kernel filters every scoped query, and PostgreSQL row-level security filters again in the database, forced on every scoped table.

The deliberate exception is the control plane — platform identity, sessions and billing — which does not belong to any workspace. Enquiries sent to the platform operator own website live there too, which is why a customer who cannot find one of them in their workspace is looking at isolation working rather than at a fault.

> **Note** A new scoped table needs row-level security applied in the same release. Creating the table at boot does not create the policy; that comes from an operational script, and a test fails on any new table that repeats the omission.

## What each client gets, separately

| Thing | Separate per client? | Note |
|---|---|---|
| Mailboxes and their credentials | Yes | Credentials are sealed on save and never echoed back to a screen |
| Knowledge and Facts | Yes | One client answers cannot ground another client replies |
| Memory, at all four tiers | Yes | Workspace, channel, endpoint, contact — all inside the boundary |
| Autonomy settings | Yes | So one client can run autonomously while another is held |
| Relationships, deals, cases, follow-ups | Yes | A person known to two clients is two records, correctly |
| The daily allowance | Yes | Consumption is bounded per workspace |
| Feature set | No — identical | There is one implementation; the difference between accounts is commercial, not functional |

## How an agency actually works day to day

1. Create a workspace per client and connect that client mailboxes and numbers inside it.
   - Result: Everything downstream — records, memory, allowances — is scoped by construction rather than by convention.
2. Set autonomy per client, and narrow it per mailbox where the client is sensitive. Scopes resolve contact, endpoint, channel, workspace.
   - Result: A cautious client can be held while a confident one runs, with no shared switch to get wrong.
3. Sign in with Google. There is no password login.
   - Result: Access follows the Google account, so removing somebody removes their route in without a shared credential to rotate.
4. Work each client Needs You queue separately.
   - Result: Approvals are recorded against the person who gave them, per workspace, which is what an agency needs when a client asks who approved something.

> **Careful** Signing in as somebody else ends the session it replaced **on that browser only** — never that person other devices. On a shared agency machine that is the behaviour you want; do not read it as a global sign-out.

## What you cannot do across clients

- **No cross-client view.** These sources document no aggregated screen across workspaces, and this page does not invent one. Every screen answers inside one workspace.
- **No shared Knowledge library.** Grounding is per workspace, so a briefing document that ought to apply to five clients is added five times, on purpose.
- **No shared memory or suppression list.** A do-not-contact entry is a fact about one workspace relationship with one person.
- **No moving records between workspaces.** Nothing here documents a transfer, so treat the workspace choice at setup as a decision that stays made.

None of this is friction that better tooling would remove. It is the same property that makes it safe to run two competitors from one product, and an agency that wants the roll-up usually finds, on inspection, that it wanted the roll-up more than it wanted the separation.

## Cost, per client

Each workspace is bounded by its own allowances, so consumption is attributable per client without any tagging discipline on the agency side. Voice is the variable that moves: audio tokens dominate, calls are priced individually, and a client whose work is phone-led will not resemble one whose work is inbox-led however similar the retainer looks.

## Questions

### Can one login reach several client workspaces?

Access is by Google account and membership, and every screen answers inside one workspace at a time. What does not exist is a screen that shows several clients at once — the three filtering layers make that a structural property rather than a missing feature.

### Is a client data really separated, or just filtered in the interface?

Separated at three levels: the allowlist refuses paths, the ORM kernel filters queries, and row-level security filters in PostgreSQL itself, forced on every scoped table. A defect in one layer does not become an exposure, which is the entire reason for having three.

### What if a record ends up in the wrong workspace?

A row stamped with an empty workspace is invisible rather than missing — it matches no scope and no policy, so it drops out of listings while by-id actions still work. Repair is a schema-owner job done by the boot-time migration, not something a request is allowed to do, because stamping a row from a request is refused by the policy.

## Related

- [Account and access](https://connectbyjbrh.com/docs/account/)
- [Security and isolation](https://connectbyjbrh.com/docs/security/)
- [Three independent layers of tenant isolation](https://connectbyjbrh.com/research/three-layers-of-isolation/)
- [Operator and customer, as a design idea](https://connectbyjbrh.com/docs/compare/owner-vs-tenant-concept/)
- [Data that is invisible rather than missing](https://connectbyjbrh.com/research/workspace-stamp/)
- [A business with several locations](https://connectbyjbrh.com/docs/use-cases/multi-location/)

## What this page is based on

- docs-source/sources/GENERAL.md §2 and §3 — the request flow and the model
- docs-source/sources/GENERAL.md §1 — one feature set, two audiences
- docs-source/sources/GENERAL.md §10 — sign-in, sessions, credentials
- docs-source/sources/CHANNELS.md §1 — the empty workspace stamp
