Overview
Organizations enable multi-tenant functionality in your application. Users can create and join multiple organizations, each with its own members, roles, and settings.
Multi-Tenant
Users can belong to multiple organizations with different roles
Granular Roles
Six built-in roles from super_admin to viewer
Member Management
Invite, remove, and manage team members
Invitations
Email-based invites with role assignment
Organization Settings
Customizable profile, branding, and preferences
Role-Based Access
Control access to features based on member roles
Quick Start
Access the current organization and user membership in any component:
import { useOrganization, useOrganizations } from '@sylphx/platform-sdk/react'
function Dashboard() {
// Current active organization
const {
organization, // Current org data
membership, // User's membership in this org
role, // User's role (e.g., 'admin')
isLoading,
isAdmin, // Helper: role is admin or super_admin
} = useOrganization()
// All organizations user belongs to
const {
organizations, // List of all orgs
setActive, // Switch active organization
create, // Create new organization
} = useOrganizations()
if (isLoading) return <Loading />
return (
<div>
<h1>Welcome to {organization?.name}</h1>
<p>Your role: {role}</p>
{isAdmin && (
<Link href="/settings">Manage Organization</Link>
)}
</div>
)
}Active Organization
Organization Roles
Six predefined roles provide granular access control. Each role inherits permissions from roles below it in the hierarchy.
super_adminFull access to all organization settings, billing, and member management
adminManage members, settings, and most organization features
billingAccess to billing, invoices, and subscription management
analyticsView analytics dashboards and export reports
developerAccess to API keys, webhooks, and technical configuration
viewerRead-only access to organization resources
import { useOrganization } from '@sylphx/platform-sdk/react'
function AdminPanel() {
const { role, isAdmin, hasPermission } = useOrganization()
// Check specific role
if (role === 'super_admin') {
// Full access
}
// Check admin-level access (super_admin or admin)
if (isAdmin) {
// Can manage members and settings
}
// Check specific permission
if (hasPermission('billing:read')) {
// Can view billing info
}
return (
<div>
{hasPermission('members:write') && (
<InviteMemberButton />
)}
</div>
)
}useOrganization Hook
Access the current active organization and the user's membership details.
| Property | Type | Description |
|---|---|---|
organization | Organization | null | Current active organization data |
membership | Membership | null | User's membership in the organization |
role | OrganizationRole | null | User's role in the organization |
isLoading | boolean | Loading state |
isAdmin | boolean | True if role is super_admin or admin |
isSuperAdmin | boolean | True if role is super_admin |
hasPermission | (permission: string) => boolean | Check if user has specific permission |
update | (data: UpdateOrgData) => Promise<void> | Update organization details |
inviteMember | (email: string, role: Role) => Promise<void> | Invite a new member |
removeMember | (userId: string) => Promise<void> | Remove a member from organization |
updateMemberRole | (userId: string, role: Role) => Promise<void> | Update a member's role |
leave | () => Promise<void> | Leave the organization |
import { useOrganization } from '@sylphx/platform-sdk/react'
function MemberManagement() {
const {
organization,
inviteMember,
removeMember,
updateMemberRole,
isAdmin,
} = useOrganization()
const handleInvite = async (email: string) => {
await inviteMember(email, 'developer')
toast.success('Invitation sent!')
}
const handleRoleChange = async (userId: string, newRole: string) => {
await updateMemberRole(userId, newRole)
toast.success('Role updated!')
}
const handleRemove = async (userId: string) => {
if (confirm('Remove this member?')) {
await removeMember(userId)
toast.success('Member removed')
}
}
return (
<div>
<h2>Members ({organization?.members.length})</h2>
{organization?.members.map((member) => (
<div key={member.id}>
<span>{member.user.email}</span>
<span>{member.role}</span>
{isAdmin && (
<>
<RoleSelect
value={member.role}
onChange={(role) => handleRoleChange(member.userId, role)}
/>
<button onClick={() => handleRemove(member.userId)}>
Remove
</button>
</>
)}
</div>
))}
</div>
)
}useOrganizations Hook
Manage all organizations the user belongs to and switch between them.
| Property | Type | Description |
|---|---|---|
organizations | Organization[] | All organizations user belongs to |
activeOrganization | Organization | null | Currently active organization |
isLoading | boolean | Loading state |
setActive | (orgId: string) => Promise<void> | Switch active organization |
create | (data: CreateOrgData) => Promise<Organization> | Create a new organization |
delete | (orgId: string) => Promise<void> | Delete an organization (super_admin only) |
import { useOrganizations } from '@sylphx/platform-sdk/react'
function OrganizationSwitcher() {
const {
organizations,
activeOrganization,
setActive,
create,
isLoading,
} = useOrganizations()
const handleSwitch = async (orgId: string) => {
await setActive(orgId)
// App context updates automatically
toast.success('Switched organization')
}
const handleCreate = async () => {
const org = await create({
name: 'New Organization',
slug: 'new-org',
})
// Automatically becomes active
router.push('/settings/organization')
}
if (isLoading) return <Spinner />
return (
<Dropdown>
<DropdownTrigger>
<Avatar src={activeOrganization?.logoUrl} />
<span>{activeOrganization?.name}</span>
</DropdownTrigger>
<DropdownContent>
{organizations.map((org) => (
<DropdownItem
key={org.id}
onClick={() => handleSwitch(org.id)}
active={org.id === activeOrganization?.id}
>
<Avatar src={org.logoUrl} size="sm" />
<span>{org.name}</span>
</DropdownItem>
))}
<DropdownSeparator />
<DropdownItem onClick={handleCreate}>
<Plus className="w-4 h-4" />
Create Organization
</DropdownItem>
</DropdownContent>
</Dropdown>
)
}Organization Data Model
Understanding the organization data structure helps when building custom UI.
interface Organization {
id: string
name: string
slug: string
logoUrl?: string
description?: string
website?: string
createdAt: Date
updatedAt: Date
// Populated when fetching with members
members?: Membership[]
invitations?: Invitation[]
// Organization settings
settings: {
defaultRole: OrganizationRole
allowMemberInvites: boolean
requireEmailDomain?: string
}
}
interface Membership {
id: string
userId: string
organizationId: string
role: OrganizationRole
joinedAt: Date
user: {
id: string
email: string
name?: string
avatarUrl?: string
}
}
type OrganizationRole =
| 'super_admin'
| 'admin'
| 'billing'
| 'analytics'
| 'developer'
| 'viewer'
interface Invitation {
id: string
email: string
role: OrganizationRole
status: 'pending' | 'accepted' | 'expired'
expiresAt: Date
invitedBy: string
}Server-Side Usage
Access organization data in Server Components and API routes.
import { currentOrganization, auth } from '@sylphx/platform-sdk/nextjs'
import { redirect } from 'next/navigation'
export default async function OrganizationPage() {
const org = await currentOrganization()
if (!org) {
redirect('/create-organization')
}
return (
<div>
<h1>{org.name}</h1>
<p>Members: {org.members?.length}</p>
</div>
)
}Organization Context
Invitations
Handle organization invitations with built-in email sending and acceptance flow.
import { useOrganization } from '@sylphx/platform-sdk/react'
function InviteMembers() {
const { inviteMember, organization } = useOrganization()
const [email, setEmail] = useState('')
const [role, setRole] = useState<OrganizationRole>('developer')
const handleInvite = async (e: FormEvent) => {
e.preventDefault()
try {
await inviteMember(email, role)
toast.success(`Invitation sent to ${email}`)
setEmail('')
} catch (error) {
if (error.code === 'ALREADY_MEMBER') {
toast.error('User is already a member')
} else if (error.code === 'INVITATION_EXISTS') {
toast.error('Invitation already sent')
} else {
toast.error('Failed to send invitation')
}
}
}
return (
<form onSubmit={handleInvite}>
<Input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="colleague@company.com"
/>
<Select value={role} onChange={setRole}>
<option value="admin">Admin</option>
<option value="developer">Developer</option>
<option value="viewer">Viewer</option>
</Select>
<Button type="submit">Send Invitation</Button>
{/* Pending invitations */}
<div className="mt-4">
<h3>Pending Invitations</h3>
{organization?.invitations
?.filter(i => i.status === 'pending')
.map((invitation) => (
<div key={invitation.id}>
<span>{invitation.email}</span>
<span>{invitation.role}</span>
<span>Expires {formatDate(invitation.expiresAt)}</span>
</div>
))
}
</div>
</form>
)
}Pre-built Organization Components
Use our ready-made components for organization management, member lists, and organization switching.
View Components