Role Hierarchy
Roles follow a hierarchy where higher roles inherit all permissions from lower roles. The hierarchy is: super_admin → admin → specialized roles (billing, analytics, developer) → viewer.
super_admin(Super Admin)OwnerFull access to everything. Can delete the organization and transfer ownership.
admin(Admin)Manage members, settings, and most features. Cannot delete organization.
billing(Billing)Access to billing, invoices, and subscription management only.
analytics(Analytics)View analytics dashboards and export reports. No configuration access.
developer(Developer)Access to API keys, webhooks, and technical configuration.
viewer(Viewer)Read-only access. Can view resources but cannot make changes.
Permission Matrix
Detailed breakdown of what each role can do across different resource types.
| Permission | Super Admin | Admin | Billing | Analytics | Developer | Viewer |
|---|---|---|---|---|---|---|
| Organization | ||||||
| Delete organization | ||||||
| Update settings | ||||||
| View details | ||||||
| Members | ||||||
| Invite members | ||||||
| Remove members | ||||||
| Change roles | ||||||
| View members | ||||||
| Billing | ||||||
| Manage subscription | ||||||
| View invoices | ||||||
| Update payment | ||||||
| API | ||||||
| Create API keys | ||||||
| View API keys | ||||||
| Configure webhooks | ||||||
| Analytics | ||||||
| View dashboards | ||||||
| Export reports | ||||||
| Create saved views | ||||||
Checking Permissions
Use the SDK hooks to check user roles and permissions in your components.
import { useOrganization } from '@sylphx/sdk/react'
function AdminPanel() {
const { role, isAdmin, isSuperAdmin } = useOrganization()
// Check exact role
if (role === 'super_admin') {
return <SuperAdminDashboard />
}
// Check admin-level (super_admin OR admin)
if (isAdmin) {
return <AdminDashboard />
}
// Check super admin only
if (isSuperAdmin) {
return <DangerZone />
}
return <AccessDenied />
}Server-Side Permission Checks
Verify permissions in API routes and server components for security.
// app/api/organization/settings/route.ts
import {
requireOrganization,
requirePermission
} from '@sylphx/sdk/nextjs'
import { NextResponse } from 'next/server'
export async function PATCH(request: Request) {
// Require organization context + specific permission
const { organization, membership } = await requireOrganization()
await requirePermission('organization:update')
const updates = await request.json()
// Certain updates require super_admin
if (updates.settings?.allowedDomains !== undefined) {
if (membership.role !== 'super_admin') {
return NextResponse.json(
{ error: 'Only super_admin can modify domain settings' },
{ status: 403 }
)
}
}
// Apply updates
const updated = await db.organization.update({
where: { id: organization.id },
data: updates,
})
return NextResponse.json(updated)
}Always Verify Server-Side
Available Permissions
Complete list of permission strings available for checking.
organization:readorganization:updateorganization:deleteorganization:transfermembers:readmembers:invitemembers:removemembers:update-rolebilling:readbilling:writebilling:invoicesbilling:payment-methodsapi-keys:readapi-keys:createapi-keys:revokeanalytics:readanalytics:exportanalytics:create-viewssecurity:audit-logssecurity:settingssecurity:ssoChanging Roles
Admins can change member roles, with restrictions to prevent privilege escalation.
import { useOrganization } from '@sylphx/sdk/react'
function MemberRoleEditor({ member }: { member: Membership }) {
const { updateMemberRole, role: myRole } = useOrganization()
const [isUpdating, setIsUpdating] = useState(false)
// Determine which roles current user can assign
const assignableRoles = useMemo(() => {
if (myRole === 'super_admin') {
return ['super_admin', 'admin', 'billing', 'analytics', 'developer', 'viewer']
}
if (myRole === 'admin') {
// Admins can't create super_admins
return ['admin', 'billing', 'analytics', 'developer', 'viewer']
}
return []
}, [myRole])
// Can't edit your own role
const canEdit = member.userId !== currentUserId && assignableRoles.length > 0
const handleRoleChange = async (newRole: OrganizationRole) => {
setIsUpdating(true)
try {
await updateMemberRole(member.userId, newRole)
toast.success(`Updated ${member.user.name}'s role to ${newRole}`)
} catch (error) {
if (error.code === 'CANNOT_DEMOTE_OWNER') {
toast.error('Cannot change the organization owner\'s role')
} else if (error.code === 'INSUFFICIENT_PERMISSIONS') {
toast.error('You don\'t have permission to assign this role')
} else {
toast.error('Failed to update role')
}
} finally {
setIsUpdating(false)
}
}
if (!canEdit) {
return <span className="text-muted-foreground">{member.role}</span>
}
return (
<Select
value={member.role}
onValueChange={handleRoleChange}
disabled={isUpdating}
>
{assignableRoles.map((role) => (
<SelectItem key={role} value={role}>
{role}
</SelectItem>
))}
</Select>
)
}Role Assignment Rules
- • Only super_admin can promote users to super_admin
- • Users cannot change their own role
- • The organization owner (first super_admin) cannot be demoted
- • Admins can assign any role except super_admin
Custom Roles (Enterprise)
Enterprise plans can define custom roles with specific permission sets.
// Configure custom roles in SylphxProvider
<SylphxProvider
config={{
organization: {
roles: {
// Extend built-in roles
extend: true,
// Define custom roles
custom: [
{
name: 'project_manager',
displayName: 'Project Manager',
inheritsFrom: 'developer', // Start with developer permissions
additionalPermissions: [
'members:invite',
'analytics:read',
],
removePermissions: [
'api-keys:create', // Remove API key creation
],
},
{
name: 'external_auditor',
displayName: 'External Auditor',
inheritsFrom: 'viewer',
additionalPermissions: [
'security:audit-logs',
'analytics:export',
],
},
{
name: 'support_agent',
displayName: 'Support Agent',
inheritsFrom: null, // Start from scratch
permissions: [
'members:read',
'organization:read',
'support:tickets:read',
'support:tickets:write',
],
},
],
// Role hierarchy for inheritance
hierarchy: [
'super_admin',
'admin',
'project_manager',
'developer',
'support_agent',
'external_auditor',
'viewer',
],
},
},
}}
>
{children}
</SylphxProvider>