You have distributors, business units, clients, or brands, and you want one Payload build to serve all of them. Each one should see only its own data. A few people, like head office, support, or auditors, need to see across all of them. You’d also rather not run a separate install for every party.
Payload handles this with an official Multi-Tenant plugin and the access control it already has. Of the 27 Payload codebases we manage, 13 have multi-tenancy switched on as of September 2026.
This guide covers what multi-tenancy means in Payload, when you need it, how to set it up, how it combines with role-based access down to the field, how far the plugin extends, five builds that use it, and the mistakes to watch for.
- What multi-tenancy means in Payload
- Do you need multi-tenancy, or only access control?
- Start with the plugin
- Multi-tenancy and role-based access, down to the field
- How far the plugin extends
- Five builds on Payload multi-tenancy
- Mistakes to watch for
- Where Payload multi-tenancy stops
- Frequently asked questions
- What to do next
What multi-tenancy means in Payload
Multi-tenancy in Payload means one database and one codebase serving many parties, with every scoped document stamped with the tenant it belongs to. Every tenant lives in the same database, and a tenant field on each document plus a set of access rules keep one tenant’s data away from another’s.
Payload can do this cleanly because it’s an application framework and backend with a polished admin attached, and access control is part of the core. Our guide to Payload CMS covers how collections, fields, access control, and hooks fit together, if you want that foundation first.
For example, a company with three distributors can run one install. Each distributor’s orders carry that distributor’s tenant, so a distributor user who opens the orders list sees their own orders and nothing else. Head office logs into the same admin and sees all three.
The pieces are few:
- A tenants collection, one document per tenant, holding its name, slug, and any settings you add.
- A tenant field on every collection you choose to scope.
- A tenants array on each user, listing which tenants that user belongs to.
- Access rules that filter every read and write by the tenants a user belongs to.
The official Multi-Tenant plugin, @payloadcms/plugin-multi-tenant, sets up all four for you.
Do you need multi-tenancy, or only access control?
You need multi-tenancy when data belongs to one party and must stay invisible to the others. You need access control when everyone works on the same data and only their permissions differ. Larger builds usually use both, with tenancy drawing the outer walls and access control deciding what each person may do inside them.
| Situation | Tenancy | Access control |
|---|---|---|
| Several brands, each with its own pages, media, and editors | Yes | Light, per brand |
| Distributors who see only their own orders, with head office seeing all | Yes | Yes, by role and region |
| One editorial team with writers, editors, and a publisher | No | Yes |
| Regional teams editing one shared product catalogue | No | Yes, by region field |
| A software product where each customer gets a workspace | Yes | Yes, inside each workspace |
| Clients whose integrations run on their own API keys | Yes | Yes, for who may approve |
If the question is “whose data is this?”, that’s tenancy. If the question is “what may this person do with it?”, that’s access control. When you’d otherwise add a brandId or customerId field to every collection and remember to filter on it everywhere, you’re building tenancy by hand. The plugin adds that field and applies the filter for you on every read and write.
Tenancy is the wrong tool when one document belongs to several parties at once. Matija Žiberna’s decision guide on multi-tenancy and access control walks through merging four division websites into one platform, where a single product can sit in two divisions. For that shape, access control with tagging fits better, because each product can carry every division it belongs to.
Start with the plugin
The official plugin is the right starting point for almost every multi-tenant build. It adds the tenant field, a tenant selector in the admin, filtered list views, tenant-scoped relationships, and per-tenant settings. Our ten Payload use cases include a short version of this setup.
Setting it up takes seven steps:
- Install the plugin
- Create the tenants collection
- Give users their tenants
- Choose the collections to scope
- Add per-tenant settings
- Mark your super admins
- Connect the frontend and domains
1. Install the plugin
Install @payloadcms/plugin-multi-tenant at the same version as your payload package. As of September 2026 both are at version 3.90.1. If you’re starting from zero, npx create-payload-app --example multi-tenant gives you a working project with tenants, users, and pages already wired.
2. Create the tenants collection
Create a tenants collection with at least a name and a slug. Add a domains field if tenants will have their own domains. Keep create and delete on this collection to super admins only, because the plugin removes a tenant’s documents when the tenant is deleted.
3. Give users their tenants
The plugin adds a tenants array to your users collection. Each row links a user to one tenant, so a user can belong to several. Decide early who may edit that array. In our builds, only super admins can add or change a user’s tenants, which stops a tenant admin from granting themselves access to someone else’s data.
4. Choose the collections to scope
List every collection that holds per-tenant data. Pages, media, forms, and orders are the usual ones. Anything you leave out stays shared across all tenants, which is right for reference data like a product catalogue and wrong for anything private.
5. Add per-tenant settings
Globals are shared by definition, so per-tenant headers, footers, and site settings become collections with isGlobal set to true. The plugin then keeps exactly one document per tenant and skips the list view, so an editor opens their tenant’s footer directly.
6. Mark your super admins
Pass a userHasAccessToAllTenants function that returns true for the people who work across every tenant. The plugin then lets them switch tenants in the admin and skips the tenant filter for them.
7. Connect the frontend and domains
On the frontend, tenant scoping is a query. You filter by tenant.slug, and Next.js rewrites map each tenant’s domain or path to the right tenant. Payload’s localized-multitenant example combines tenant domains with locale paths in one app.
Put together, a config for a small multi-brand build looks like this.
import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant'
export default buildConfig({
// ...collections, db, and the rest of your config
plugins: [
multiTenantPlugin({
tenantsSlug: 'tenants',
collections: {
pages: {},
media: {},
'site-settings': { isGlobal: true },
},
tenantsArrayField: {
includeDefaultField: true,
arrayFieldAccess: {
create: superAdminOnly,
update: superAdminOnly,
},
},
userHasAccessToAllTenants: (user) => Boolean(user?.roles?.includes('super-admin')),
}),
],
})After setup, when a tenant’s documents are missing from the list view, check the tenant selector and confirm the document carries a tenant. When a user sees the wrong tenant, check their tenants array. When a new document lands in the wrong tenant, check which tenant was selected when it was created.
Multi-tenancy and role-based access, down to the field
In a multi-tenant Payload build, access works at three levels. The tenant decides which documents a user can reach. The role decides what they may do there. The field decides what they may see or change inside a document they can reach.
The tenant decides what a user can reach
The plugin’s filter is the outer wall. For example, in a distributor order portal we built for a hardware manufacturer’s Middle East operation, each distributor is a tenant. A distributor’s users can only reach their own orders, quotations, and cases. There’s no query a distributor can write that returns another distributor’s order, because the plugin combines its tenant filter with your access rules on every read.
The role decides what a user may do, inside and across tenants
Inside the walls, roles set permissions. Some roles live inside one tenant, like a distributor user who can submit orders but not approve them. Other roles cut across tenants on a second axis.
In the same portal, the roles look like this:
| Role | Tenant scope | Role scope | What they may change |
|---|---|---|---|
| Distributor user | Their own distributor | Their own orders and proposals | Draft and submit orders, reply on their orders |
| Sales specialist | Every distributor in their region | Orders and proposals assigned to their team | Move orders forward, correct orders with the distributor’s confirmation |
| Sales manager | Every distributor in their region | Their team’s orders and workload | Reassign orders |
| Executive | All distributors | Read only | Nothing, but sees every region |
| Administrator | All distributors | Configuration | Item master, distributor profiles, users, routing rules |
| Auditor | All distributors | Read only | Nothing |
The region is the second axis. A sales specialist isn’t a member of every tenant. Their access is computed from the region on each distributor, so moving a distributor to another region moves its orders to another team without touching a single user record.
The field decides what a user may see or change
Payload lets every field carry its own access functions for create, read, and update.
For example, the licensing platform we’re building has a licence key on each entitlement. The customer who owns the entitlement can read the key, admins can read it, and nobody else can, even staff who can read the rest of the document.
{
name: 'licenceKey',
type: 'text',
access: {
read: ({ req: { user }, doc }) =>
isAdmin(user) || doc?.customer === user?.id,
},
}In the order portal, a distributor never types a price. Unit prices are resolved from the distributor’s tier and any valid quotation, so the price field is read-only to distributors by construction. In the licensing platform, staff can update an order’s fulfilment fields, but any attempt to change its price is rejected with an access error.
Together, the three levels can express a rule as precise as “a sales specialist in this region can read this distributor’s order and change its shipping details, but not its price.”
How far the plugin extends
The plugin’s defaults fit a multi-brand website as they are. For anything larger, you extend it, and it bends a long way before you need your own access layer. We extend it in this order, and the first two steps use the plugin’s own options.
Mix shared and per-tenant collections
Few builds are entirely per-tenant. A product catalogue, a set of message templates, or a list of exception categories is usually shared, while the orders and cases that use them belong to one tenant. Leave shared collections out of the plugin’s list, or keep them in and set useTenantAccess to false when you want the tenant field but your own access rules.
Replace the default filter where you need to
When the plugin’s filter is too strict for one collection, accessResultOverride lets you adjust its result for that collection alone. It receives the original result and the operation, like read or update, so you can widen reads and leave writes alone. For example, distributors could read the order templates head office publishes for every tenant, while only head office can change them. When you need the tenant field in a particular place in your schema, customTenantField lets you place it yourself.
Carry the tenant into hooks, the Local API, and jobs
The admin panel is only one way in. Server code, background jobs, and payment callbacks also create and read documents, and each one has to know its tenant. For example, in a marketplace where each store is a tenant, an order’s tenant comes from the store that owns the products in the cart, not from the shopper. A guest checkout has no user, but its products still belong to a store, so the order and its invoice land with that store. A cart that holds two stores’ products becomes one order per store, and a hook on order creation is where that split and the tenant stamp happen.
Gate features per tenant
Tenancy can also decide which features a tenant has. The licensing platform unlocks portal modules through entitlements, and every entitlement lookup filters by tenant and fails closed. A module is a route, a navigation item, and a set of tenant-scoped collections, so adding a second tenant needs no changes to any module.
Give each tenant its own domain, locale, and settings
Once the data is scoped, the frontend follows. Each tenant can have its own domain, its own locales, and its own header, footer, and theme settings through isGlobal collections.
Five builds on Payload multi-tenancy
The five builds below all run on the plugin. Each one leans on a different way of extending it. If you’re still choosing a platform, our Payload vs Sanity comparison covers why code-level access control suits builds like these.
1. A distributor order portal with an AI agent in the loop
A hardware manufacturer’s Middle East operation took orders from its distributors by emailed spreadsheets. We built them a portal on Payload where each distributor is a tenant.
Per tenant: orders and their lines, cases, exceptions, messages, quotations, the distributor’s own part-number mappings, and the distributor’s users.
Shared: the item master, message templates, exception categories, and system settings.
What carries the tenant: the region axis for sales teams, a read-across role for executives, and a read-only role for auditors. An AI agent handles routine exceptions, like a quantity below the minimum or an expired quotation, by messaging the distributor inside the case. The agent can only act through defined tools, so it can read the order, draft from an approved template, propose a correction, or escalate, and it can’t send anything free-form. A review switch lets the operations owner hold every agent draft for approval, globally or per exception type. Every state change, message, and agent run is written to an append-only event log, and management watches it all on one dashboard across every distributor.
Best for: manufacturers and wholesalers replacing email and spreadsheet order intake, where distributors must never see each other’s pricing.
2. A licensing platform growing into distributor pricing
A software business sells licences online and is moving to a model where distributors log in, buy, and see their own prices. We’re building it on Payload.
Per tenant: every collection of Payload’s official e-commerce plugin, from products and variants to carts, orders, transactions, and addresses. Entitlements, and the data behind each portal module.
Shared: the platform code and the module definitions.
What carries the tenant: entitlements. Each entitlement grants a licence or a module, sets its own expiry from the product’s term, and holds a licence key that only its owner and admins can read. Distributor-specific pricing is the next phase, and the tenant model is already in place for it. Keep in mind that the e-commerce plugin is still in beta, and it doesn’t handle shipping, taxes, or subscriptions for you.
Best for: software and content businesses that sell licences and plan to sell through resellers or distributors.
3. Integrations per tenant: invoicing, CRM, and messaging
When each client or business unit has its own accounts with outside systems, the integration data becomes per-tenant data. We scope it that way on every build.
Per tenant: credentials for each outside system, the workflows that use them, pending approvals, execution logs, and inbound messages.
Shared: the integration code.
What carries the tenant: the workflow. Each workflow belongs to one tenant and points at a credential picked from that tenant’s own credentials, since the plugin scopes the picker too. So when a background job raises an invoice or updates a CRM record, it runs on that client’s account. A workflow can pause at an approval step, which parks the action in a pending-approvals collection until a person approves it, and the execution log records what ran and what came back.
Best for: agencies and groups that run automations for several clients or companies from one backend.
4. AI assistants with their own knowledge and tools
An assistant that answers for one brand must never quote another brand’s documents. In our voice-agent builds, the agents, their knowledge, their tools, and their call logs are all tenant-scoped collections.
Per tenant: agent definitions, the knowledge they answer from, the tools they may call, and the log of every tool call.
Shared: the agent runtime.
What carries the tenant: the agent. Each agent belongs to one tenant, and every knowledge lookup and tool call has to carry that tenant into its query. Collection scoping protects the admin panel, but an agent’s lookup usually runs through the Local API from a server route. For example, a knowledge search needs the agent’s tenant in its where clause, or it reads every tenant’s entries that match the query.
Best for: teams offering AI assistants to several clients or brands, where each assistant’s knowledge is confidential.
5. Team workspaces and internal tools
Internal tools often need the tenant to be a team rather than a customer. We run a workspace product on Payload where each team is a tenant.
Per tenant: workspaces, memberships, invites, shared databases and their rows, automations, and team settings.
Shared: the product itself.
What carries the tenant: memberships. A person joins a team by invite, and whatever the team shares, like a database or an automation, is shared only inside that team. Employee dashboards follow the same pattern, with departments or companies in a group as tenants and a read-across role for leadership.
Best for: internal tools, employee portals, and team products where departments or subsidiaries must keep their data apart.
The five builds at a glance
| Build | The tenant is | Per tenant | Shared | Carries the tenant |
|---|---|---|---|---|
| Distributor order portal | A distributor | Orders, cases, quotations, messages | Item master, templates | Region axis, tool-bound agent, event log |
| Licensing platform | A business, distributors next | Commerce collections, entitlements | Platform, module definitions | Entitlements |
| Integrations | A client or business unit | Credentials, workflows, approvals, logs | Integration code | The workflow and its credential |
| AI assistants | A client or brand | Agents, knowledge, tools, logs | Agent runtime | The agent’s tenant, in every lookup |
| Team workspaces | A team or company | Workspaces, memberships, shared data | The product | Memberships |
If one of these is close to what you’re planning, the tenant model is the decision to settle first. We cover how we shape that on our Payload development service page.
Mistakes to watch for
Multi-tenant bugs tend to be quiet. Nothing crashes, and one tenant sees something it shouldn’t, or can’t see something it should.
A Local API call that skips access control
Payload’s Local API skips access control by default. A server-side lookup that doesn’t set overrideAccess to false can return another tenant’s document. For example, in the licensing platform’s customer portal, an order page fetched this way rendered another customer’s order during testing. The fix was one line, plus a test that proves someone else’s order returns a not-found page.
const order = await payload.findByID({
collection: 'orders',
id,
user,
overrideAccess: false,
})A server call made with a user who has no tenants
Pass the full user document into Local API calls, tenants included. A trimmed user object without its tenants array gets no access at all to tenant-scoped collections, so every read is refused. In the same portal, the session helper returned a partial user, and every order query failed until the pages read the full user instead.
A new collection left off the plugin’s list
A collection you add later isn’t scoped until you add it to the plugin’s collections. Until then it’s shared across every tenant. Make registration part of the checklist for any new collection.
Slugs that are unique across the whole database
Two tenants will both want an about page. Make slugs unique per tenant with a compound unique index on the tenant and slug fields, so each tenant can publish its own about page.
Self-registration that creates a user with no tenant
If customers can sign up themselves, the new user needs a tenant at creation, usually the store or site they signed up on. Set it in a hook from the domain the request came in on, or the user logs in to an empty account.
A custom tenant field that doesn’t recognise super admins
If you place the tenant field yourself with customTenantField, pass your userHasAccessToAllTenants function to it as well. Otherwise, super admins’ writes to tenants they aren’t directly assigned to are rejected.
Weak access on the tenants collection
Deleting a tenant deletes its documents by default. Keep delete on the tenants collection to super admins, and turn off cleanupAfterTenantDelete if you’d rather archive a tenant than remove its data.
Where Payload multi-tenancy stops
The plugin gives you logical isolation inside one database, which covers most builds.
Every tenant shares the same schema. If one tenant needs fields or collections the others must not have, you either add them for everyone and hide them, or you run that tenant separately.
Every tenant shares the same database. When a contract or a data-residency rule requires a client’s data to sit in its own database or its own region, that client gets its own deployment of the same codebase.
Every query filters by tenant. The plugin already indexes the tenant field, so as tenants grow, add compound indexes for the fields you query alongside it, like tenant and slug, and watch your slowest queries.
