
Multi-Tenant Architecture for B2B SaaS — Close Enterprise Deals
Org/team/seat RLS, SCIM provisioning, custom domains, and per-tenant audit and export — architected so enterprise procurement stops being a code rewrite each quarter.
The problem
B2B SaaS architecture fails at the moment the first enterprise prospect lands in the pipeline. The MVP shipped with one user per account and a single org_id column, which was the right call when the customer base was prosumers and small teams. Then an enterprise buyer enters procurement and the questions start: Can we get SSO with our Okta tenant? Do you support SCIM for auto-provisioning and deprovisioning? Can each of our departments have its own workspace inside our account? Do you support custom domains so our internal users see app.acme.com instead of yourtool.com/acme? Can you give us a per-tenant audit log we can hand our SOC 2 auditor? When a user leaves our company, can your system delete their data within thirty days, and can you prove it? Can we export our data in a structured format for GDPR Article 20 portability requests our European entity is required to honor?
Forked-per-deal enterprise plumbing
Each one of those questions is a six-figure ACV gating item, and each one assumes a tenancy model that supports more than one shape of customer at the same time. The team that built for prosumer accounts hits this wall and starts forking — a special branch for the enterprise customer, a one-off SCIM integration, a manual export script the support team runs once a quarter, an audit log table that captures some actions but not others. Six months later the engineering org is half-time on enterprise plumbing and the data model has three different ways of representing a "team" because the term meant different things in different sales conversations.
Recurring obligations, not one-time asks
The deeper problem is that the questions enterprise buyers ask are not optional and they are not one-time. SOC 2 auditors come back every year. GDPR data subject requests arrive whenever an EU employee asks for their data. New enterprise customers want their own custom domain, their own SCIM endpoint, their own audit log scoped to their org. The tenancy model has to absorb this without a code change per customer, which means the model has to be designed for it from the start — or refactored once into a shape that holds, instead of patched per deal.

What changes for your business
Org, team, and seat scopes under RLS
A B2B-ready multi-tenant architecture treats organizations, teams, and seats as three independent scopes layered on top of each other, with every tenant-scoped table carrying the org_id that determines its tenancy and an optional team_id for resources that belong inside a specific team rather than the whole organization. Postgres Row Level Security enforces both scopes — a forgotten filter in application code becomes an empty result set instead of cross-tenant data, because the Postgres documentation describes RLS as a per-user restriction evaluated before the query runs, and a properly forced policy applies even when the table owner is the one querying. The same RLS layer is what makes shared services safe: search, analytics, and the audit log all read tenant scope from the JWT instead of trusting the application code to remember it.
Tenant-scoped SCIM provisioning
The provisioning surface absorbs the enterprise checklist without forking. SCIM 2.0, as specified in RFC 7644, gives the customer's IdP a standard endpoint for creating, updating, and deactivating users — but the spec explicitly delegates tenant-isolation mechanics to the service provider, so the SCIM endpoint your app exposes has to map every authenticated SCIM client to a tenant before it touches a user record. Done correctly, this means each enterprise customer points their Okta or Azure AD at a tenant-scoped SCIM URL and provisions seats automatically, and the same RLS policies that protect everything else also protect the user records SCIM is writing.
Custom domains, audit logs, and exports as configuration
Custom domains map to the org row, terminated at the edge with TLS that the platform provisions (Cloudflare for SaaS, AWS ACM, or a per-cert pipeline depending on volume), with middleware that resolves the host header to a tenant before any route handler sees the request. Audit logs are append-only and scoped to the tenant at write time, queryable by user and resource and date range, exportable in a SIEM-ingestable format. Data exports run from a single endpoint scoped by RLS, producing the structured, machine-readable bundle GDPR Article 20 requires — JSON or CSV per table, referential keys preserved, the same shape regardless of which customer requested it.
The outcome for the business: the enterprise checklist stops being a series of forked code branches and becomes configuration on the org row. The next thousand-seat prospect does not require a sprint. The SOC 2 auditor's questions about logical access, audit trails, and access reviews have one architecture to point at, not three. Procurement security questionnaires get filled out faster because the answers are structural rather than case-by-case. The team ships features instead of plumbing.

What gets shipped for B2B tenancy
The work for a B2B SaaS at this intersection lands in a predictable shape. The tenancy schema goes in first — organizations, teams, memberships, roles, and the org_id and team_id columns on every tenant-scoped table, with indexes set up so RLS policies push down into index scans. RLS policies layer the three scopes: org-level for resources that belong to the whole organization, team-level for resources scoped inside a team, seat-level for resources owned by a specific user. FORCE ROW LEVEL SECURITY ships on every tenant-scoped table because Postgres table owners would otherwise bypass policies silently, and a CI check fails any migration that adds a tenant-scoped table without the FORCE clause.
The auth layer extends to enterprise needs: JWT claims carry org_id, team_ids, and role; the auth provider supports SAML for SSO and SCIM for provisioning; refresh tokens rotate and revoke session-wide on reuse. The SCIM endpoint exposes /Users and /Groups per RFC 7644, with tenant resolution happening at the bearer-token validation step so the IdP can only see and modify users inside its own org. SAML metadata is per-tenant, so each enterprise customer's IdP integration is configuration on their org row rather than a code change.
Custom domain support ships as a hostname column on the org row, an edge-layer TLS pipeline, and middleware that resolves the host to a tenant. Audit logging is an append-only table written from a single chokepoint in the request pipeline, capturing actor, action, resource, before-state, after-state, IP, timestamp, with a query API scoped by RLS to the tenant's events only. The export endpoint walks the tenancy schema, runs under the same RLS policies, and produces a downloadable bundle in the structured, machine-readable format GDPR Article 20 requires.
```sql -- Org + team RLS, with FORCE so admins and migrations cannot silently bypass alter table public.documents enable row level security; alter table public.documents force row level security;
create policy doc_org_scope on public.documents for all using ( org_id = ((auth.jwt() -> 'app_metadata') ->> 'org_id')::uuid ) with check ( org_id = ((auth.jwt() -> 'app_metadata') ->> 'org_id')::uuid );
The deliverable is working code in your repository — schema, policies, SCIM endpoint, SAML integration, custom domain wiring, audit table and query API, export endpoint — plus a runbook for the failure modes that actually occur (RLS bypassed because FORCE was not set, SCIM provisioning the wrong tenant because token-to-org mapping was missing, custom-domain TLS expiry not alerting, audit log query returning cross-tenant rows because a policy referenced raw_user_meta_data instead of raw_app_meta_data).
What enterprise procurement asks first
The questions a security or procurement team at a thousand-employee buyer asks during evaluation cluster into a small set. Where is our data stored, and how is it isolated from other customers? Answer: dedicated org scope enforced at the database layer through Postgres RLS with FORCE, not application-layer filtering. Can we use our existing identity provider? SAML for sign-in plus SCIM for provisioning, with the SCIM endpoint tenant-scoped per RFC 7644's authorization requirement. Can we get an audit log we can export to our SIEM? Yes, scoped to your org, queryable by user and resource and date range, exportable to JSON or CSV. What happens when an employee leaves our company? SCIM deprovisioning revokes the seat immediately; the audit log retains the historical actions for whatever retention period your compliance regime requires. Can you respect a GDPR or CCPA data subject request without a custom integration? Yes — the export endpoint produces the structured bundle Article 20 specifies, and the deletion endpoint runs under the same RLS scope. These are not custom answers per customer; they are the same architecture answering different procurement questions.
Common failure modes for B2B tenancy
Four failure modes show up repeatedly. The first is SCIM provisioning that does not resolve the tenant from the calling IdP's credentials, so a misconfigured SCIM bearer token lets one customer create users inside another customer's org. RFC 7644 requires the service provider to map every authenticated client to an access control policy, and the architecture has to enforce that mapping at the first byte of every request. The second is custom domain TLS that expires without alerting, breaking the customer's login flow at the worst possible time — the cert pipeline needs monitoring and auto-renewal, not just initial provisioning. The third is the audit log that captures user actions but not service-account actions, so background jobs and admin operations disappear from the record and the auditor flags it. The fourth is the export endpoint that runs as a privileged user instead of under the tenant's RLS scope, which works fine until the day it returns rows from the wrong tenant in a customer-facing export. Each one is preventable; each one shows up in B2B SaaS post-mortems because the original tenancy build did not assume them.
How BoostFrame approaches this
BoostFrame Enterprise AI runs the architecture described above across seven production applications in the same suite, with shared identity, RLS-enforced tenant isolation, and per-tenant billing wired into Stripe through an idempotent webhook pattern — the same stack that has generated 200K+ AI-assisted keywords, run 1,500+ AI scans, and automated 7,000+ sites for paying customers. The B2B-specific extensions — SCIM, custom domains, per-tenant audit, per-tenant export — are scoped against your enterprise checklist rather than built speculatively, and the engagement leaves your team able to ship the next enterprise feature without calling an outside engineer back in. The author is Bill Fackelman, co-founder and CTO of BoostFrame Enterprise AI.
Outcomes you should expect
What this delivers
- Close enterprise deals without rewriting your data model each quarter — the org/team/seat model holds from your first ten-seat plan to your first thousand-seat one.
- Pass the SOC 2 and procurement security review without rebuilding logging, audit, and export pipelines mid-cycle — they get scoped per-tenant from the start.
- Ship custom domains, SAML/SCIM provisioning, and tenant-scoped audit logs as configuration instead of forked code branches per enterprise customer.
- Stop the cross-tenant leak class of incident structurally — RLS-enforced isolation makes a forgotten WHERE clause return zero rows instead of another customer's data.
Industry data
By the numbers
RFC 7644 (SCIM 2.0) requires that 'the SCIM service provider MUST be able to map the authenticated client to an access control policy in order to determine the client's authorization to retrieve and update SCIM resources' — which means the provisioning endpoint itself has to know which tenant the calling IdP belongs to before it touches any user record.
RFC 7644 Section 6 acknowledges multi-tenancy but explicitly does not define the data-isolation mechanisms — the spec delegates tenant separation to the service provider, so SCIM compliance alone does not give you tenant isolation.
Postgres Row Level Security supports hierarchical policies — the official docs show a pattern where a row's visibility is computed from a group_id joined back to the current user's group membership, which is the same shape an org/team/seat model takes.
Postgres table owners normally bypass row security, so any tenant-scoped table needs ALTER TABLE ... FORCE ROW LEVEL SECURITY explicitly set — otherwise migrations and admin queries silently see every tenant's data while the policy looks correct in code review.
Supabase documentation warns that storing tenant or role claims in raw_user_meta_data is unsafe because the authenticated user can update it via supabase.auth.update() — authorization claims belong in raw_app_meta_data, which only the service role can write.
GDPR Article 20 requires that personal data be portable in a 'structured, commonly used and machine-readable format' — for a B2B SaaS this means the data-export feature your enterprise customers ask about during procurement has to produce something parseable, not a PDF dump.
Live in production today
The same engineering, shipped in production at BFEAI.
I'm co-founder & CTO of Be Found Everywhere (BFEAI), a 7-app AI SaaS platform running today. The work I deliver for clients is the work I do every week on my own platform.
7
Production apps
200K+
Keywords generated
1,500+
AI scans run
7,000+
Sites automated
Common questions
What buyers ask before reaching out
What's the right tenancy model for a B2B SaaS — one tenant per customer, or org-plus-teams-plus-seats?
For a B2B SaaS aimed at companies above the freelancer tier, the three-level model — organizations contain teams contain seats — is almost always the right call, even if your first ten customers only use one team. The cost of adding the team layer later, after you have paying enterprise accounts that need departmental separation, is much higher than the cost of carrying it from day one. The tenancy column on every table is org_id; team_id is a secondary scope your RLS policies can layer on for resources that belong to a single team rather than the whole org.
Do we need SCIM if we already have SAML SSO?
SAML handles authentication — when a user shows up, is this person who they claim to be. SCIM handles provisioning — when someone gets hired or fired, does our system know without an IT ticket. Enterprise buyers in regulated industries treat them as one package, because the audit finding 'former employees still have active accounts' fails compliance. SCIM lets the customer's IdP create, update, and deactivate seats in your app without your support team in the loop. RFC 7644 spells out the protocol; the part you have to add yourself is mapping the calling IdP to the right tenant before any user record is touched.
How do custom domains per enterprise customer actually work without forking the app?
Each enterprise tenant maps to a hostname stored on the organization row. A wildcard-or-multi-domain TLS setup (Cloudflare for SaaS, AWS ACM with SNI, or a custom certificate pipeline) terminates the connection and forwards the host header to your app. Middleware reads the host, looks up the tenant, and stamps the tenant context onto the request before any route handler runs. The app code itself doesn't know or care about hostnames — it reads tenant scope from the same JWT-and-RLS layer every other request uses. The work is in the cert and DNS automation, not in the application.
Our enterprise prospects keep asking for per-tenant audit logs for their SOC 2. What does that mean architecturally?
It means an append-only log scoped to the tenant, capturing who did what to what data, with timestamps, IP addresses, and enough resource context that the customer's compliance team can answer 'show me every action user X took on object Y between dates A and B' without your support team in the loop. The hard part isn't the writes — it's that the read path also needs to be tenant-scoped (the customer should not see another customer's audit events) and exportable in a format their SIEM can ingest. Architecting this from the start is the cheap version; bolting it on after a customer asks costs a sprint per shape of question they ask.
How does per-tenant data export for GDPR or CCPA work without a custom script per customer?
One export endpoint, parameterized by tenant scope, that walks the tenancy schema and produces a structured, machine-readable bundle — GDPR Article 20 specifies the format requirement. The endpoint runs under the same RLS policies as the rest of the app, so it cannot accidentally include another tenant's data. The output is typically a zip of JSON or CSV per table, with referential keys preserved so the customer's downstream tooling can rebuild the object graph. The same endpoint covers both the GDPR portability request and the CCPA 'right to know' request because the underlying data is the same.
What about shared services like search or analytics — how do we avoid leaks there?
Shared services are where most multi-tenant leaks happen, because they sit outside the RLS boundary. The pattern is tenant scoping at the query layer of every shared service: every search index document carries an org_id field that the query layer filters on as a required clause, every analytics warehouse table has a tenant column that downstream queries join through, and the layer that builds and pushes those scopes is the same layer that issues the JWT. If a service can't enforce tenant scope at query time — some vector databases historically couldn't — the answer is one index per tenant, not 'we'll filter results after retrieval.'
Can a B2B SaaS migrate from a single-tenant or undertested-tenancy model to this without breaking production customers?
Yes, and most do — greenfield B2B SaaS at the stage where they need this engagement is rare. The migration shape is: add org_id and team_id columns where missing, backfill from existing user-to-account mappings, add RLS policies behind a feature flag, run a shadow audit that compares query results with and without enforcement, then flip enforcement on. The risky step is the backfill, which gets its own checkpoint and rollback path. The migration is usually slower than a greenfield build, but the cutover itself is fast once the backfill is verified.
What does an engagement like this typically run in time?
For a seed-to-Series-A B2B SaaS, the full tenancy build — org/team/seat RLS, SCIM provisioning, custom domain support, per-tenant audit logging, per-tenant export endpoint — typically runs four to six weeks of focused work. Mid-flight refactors of an app with paying enterprise customers run longer because the data migration and cutover need their own plan. The bottleneck is usually how clean your current user-to-account mapping is, not the Postgres or auth surface area.
Ready to see if this is a fit?
A 15-minute call. No deck, no slides. We talk about what you're shipping and where engineering is the bottleneck. Either way, you walk away with a senior engineer's read on your situation.