Guild Multitenancy Architecture
Overview
Guild uses @payloadcms/plugin-multi-tenant with Organization as the tenant boundary. This was implemented in April 2026 on branch feat/multi-tenant-plugin.
Data Hierarchy
Organization (business entity, e.g. "Script Wizards")
→ 1 Stripe account (all subscriptions/billing)
→ N Shops (physical locations, e.g. "Dungeon Books JC")
→ each Shop has its own Square account/location
→ each Shop has its own Tiers (Stripe prices under the org's account)
→ Purchases, CheckIns, LoyaltyTransactions are shop-scoped
→ Members belong to the org, tied to a specific shop via their tier
Role System
Two-level roles:
- Global:
super-admin(platform operator) vsuser(everyone else) - Per-tenant:
admin(org owner like Phil) vsstaff(employees)
Super-admins see all tenants. Users only see orgs they’re assigned to via the tenants array on the Users collection.
Payment Provider Mapping
| Level | Stripe | Square |
|---|---|---|
| Organization | 1 Stripe account, customer records, subscriptions | — |
| Shop | Tier prices (under org’s Stripe) | 1 Square location, POS transactions |
Membership Model
- Members are org-scoped (plugin injects
organizationfield) - Members are implicitly shop-scoped through their tier (tiers belong to a shop)
- A member at Shop A picks Shop A’s tiers. A member at Shop B picks Shop B’s tiers. Both under the same org’s Stripe account
- Multi-shop membership (one subscription covers all locations) is a future product decision, not needed in the data model today
Domain Routing (planned)
Each shop will have a domain field. Next.js middleware resolves hostname → shop → org.
dungeon.club → Shop "Dungeon Books JC" → Org "Script Wizards"
club.victorypointjc.com → Shop "Victory Point JC" → Org "Victory Point"
Frontend pages (signup, dashboard, tiers) will read the resolved shop to show the right branding, lingo, and tiers.
Onboarding a New Org
- Create Organization in admin panel
- Create Shop under that org (with Square location ID)
- Create Tiers for the shop (with Stripe price IDs from org’s Stripe account)
- Create User with
role: user,tenants: [{ tenant: orgId, roles: ['admin'], shop: shopId }]
Key Decisions
- Organization is the tenant, not Shop — keeps admin boundary clean for multi-location owners
- Plugin field named
organization(tenantField.name: 'organization') to match existing field names and minimize code changes - No domain field on Organization — domain routing is at the shop level since shops are customer-facing
- Points never transfer between shops — each shop has its own loyalty economy
Related
- guild-competition — multi-guild platform vision built on this model
- guild-quest-hub-network — partner kiosk host model
- membership-platform — full system architecture