How do I keep client data separate?
There are three levels: a filter in the application, a rule inside a shared database, or a database per client. They fail very differently.

The short answer
- There are three levels of separation, and they are not variations on one idea: a filter written in application code, a row-level rule enforced inside one shared database, or a separate database per client.
- Application-level filtering is the weakest because a single query that forgets its filter exposes another client's records, and nothing below the application is there to stop it.
- Row-level security is genuinely stronger because the rule lives in the database and applies to every query, but it is still one database, so a policy written wrongly is still a cross-client exposure.
- A database per client removes the failure mode rather than defending against it: there is no shared table for a query to reach across, the export is the whole database, and the storage region is chosen per client.
Why the three levels fail differently
Start with what "separate" is protecting you from. Not a hacker, usually. The realistic risk is an ordinary mistake: a query that forgot a filter, a permission set copied from the wrong template, a support tool that lists more than it should. What decides the damage is how far a mistake travels before something stops it.
With application-level filtering, every client's rows are in the same tables and the only thing keeping them apart is code that remembers to say which client is asking. It usually works. When it does not, one missing condition returns another client's records, and nothing underneath the application is there to catch it.
Row-level security moves the rule into the database, so it applies to every query whether the developer remembered or not. That is a real improvement and it is what a careful multi-tenant product does. The limit is structural: the rule and the data live in the same place. A policy written too loosely, or a credential with enough privilege to bypass policies, still reaches every client at once, because every client is there to reach.
A database per client changes the shape of the problem. There is no shared table, so there is no query that could cross between two clients even if someone wrote one. A mistake made in one client's project affects that client. The blast radius is one account, by construction rather than by policy.
The three levels, compared on what actually differs
Read the failure row first. It is the one that decides the other four.
| Filter in the app | Row-level rule in a shared database | A database per client | |
|---|---|---|---|
| Where the rule lives | In application code, re-applied on every query a developer writes | In the database, applied to every query automatically | Nowhere. There is no shared data to write a rule about |
| What one mistake exposes | Another client's records, with nothing below the app to stop it | Potentially every client, if the policy itself is wrong or is bypassed | One client's own account, because that is all the project contains |
| What an export contains | Files pulled out of larger shared tables | Files pulled out of larger shared tables | The whole database, relationships intact |
| Data residency | Set for the whole deployment, not per client | Set for the whole deployment, not per client | Chosen per client when their project is created |
| The real cost | Cheapest to run and fastest to open a new client | Cheap to run, and a well-built version of the common approach | One set of infrastructure per client, so pricing is per instance rather than per seat |
What to do about it as an agency
Decide first whether you need the third level at all. If your clients are small, do not hold regulated data and have never asked where their records live, the second level is a reasonable answer and it costs less. Be honest about which situation you are in rather than buying isolation as insurance you cannot explain.
If you do need it, verify it with the three questions rather than a marketing page. Do two of my clients' records ever sit in the same table. Does an export give me a database or a folder of files. Can one client's data live in a different region without moving everyone. A vendor who separates properly answers all three quickly, because the answers come from the architecture rather than from a policy document.
Disclosure: SalesCrew is our product and it is built on the third level. Each client instance is its own Supabase project, meaning its own Postgres database, its own file storage and its own edge functions, with the region chosen when the project is created. Access control inside an instance uses row-level security plus a three-axis scope model applied identically to the interface and to MCP tokens, and every tool call is written to an audit log with the actor named. The cost of this is real and worth stating: one set of infrastructure per client is why pricing is per instance, and a change you want on every client is a change applied to each of them.
Branding and isolation are unrelated features
Questions
- Is row-level security enough to keep clients separate?
- It is a real control and much stronger than filtering in application code, because the rule sits in the database and applies to every query. Its limit is that it is still a rule inside one database: it protects against a query that forgets a filter, not against a policy written wrongly or a credential with enough privilege to bypass it. Separate databases remove that class of mistake instead of guarding against it.
- Does data residency work differently on a shared platform?
- Usually it is a platform-wide or region-wide setting rather than a per-client one, because every client in that deployment shares the same database. If one client needs their data in a specific region, a shared platform can only answer by running a whole separate regional deployment. With a database per client, the region is chosen for that client when their project is created.
- What should I ask a vendor to confirm how separated my clients are?
- Ask three questions and write down the answers. Do two of my clients' records ever sit in the same database table. If a client asks for an export, do I get their database or a set of files pulled out of a larger one. And can I choose a different storage region for one client without changing anything for the others. The answers to those three tell you the architecture regardless of what the marketing page says.