System Roles
Tenant Roles
Four system roles are seeded for every tenant at creation time: owner,
admin, member, viewer. Each role’s permissions are fixed by FlowState
and cannot currently be customized per tenant.
Role Matrix
| Permission | owner | admin | member | viewer |
|---|---|---|---|---|
tenant:settings:read | ✓ | ✓ | ✓ | ✓ |
tenant:settings:write | ✓ | ✓ | ||
tenant:members:read | ✓ | ✓ | ✓ | ✓ |
tenant:members:invite | ✓ | ✓ | ||
tenant:members:remove | ✓ | ✓ | ||
tenant:members:role:write | ✓ | ✓ | ||
tenant:grants:read | ✓ | ✓ | ||
tenant:grants:write | ✓ | ✓ | ||
tenant:keys:read | ✓ | ✓ | ||
tenant:keys:read:self | ✓ | ✓ | ✓ | |
tenant:keys:write | ✓ | ✓ | ✓ | |
tenant:billing:read | ✓ | ✓ | ||
tenant:billing:write | ✓ | ✓ | ||
tenant:gdpr:read | ✓ | ✓ | ||
tenant:transfer:owner | ✓ | |||
tenant:delete | ✓ | |||
tenant:features:read | ✓ | ✓ | ||
tenant:features:write | ✓ | ✓ | ||
tenant:integrations:hubspot:connect | ✓ | ✓ | ||
tenant:integrations:hubspot:disconnect | ✓ | ✓ | ||
tenant:integrations:hubspot:read | ✓ | ✓ | ✓ | ✓ |
tenant:integrations:slack:read | ✓ | ✓ | ✓ | ✓ |
tenant:integrations:slack:write | ✓ | ✓ | ||
tenant:integrations:apn:configure | ✓ | ✓ | ||
tenant:integrations:apn:read | ✓ | ✓ | ✓ | ✓ |
tenant:hubspot:read | ✓ | ✓ | ✓ | |
tenant:deals:analyze | ✓ | ✓ | ✓ | |
tenant:deals:read | ✓ | ✓ | ✓ | ✓ |
tenant:deals:reanalyze | ✓ | ✓ | ✓ | |
tenant:goals:read | ✓ | ✓ | ✓ | ✓ |
tenant:goals:write | ✓ | ✓ | ✓ | |
tenant:win-loss:read | ✓ | ✓ | ✓ | ✓ |
tenant:win-loss:trigger | ✓ | ✓ | ||
tenant:slack:test | ✓ | ✓ | ||
tenant:apn:read | ✓ | ✓ | ✓ | ✓ |
tenant:apn:sync:trigger | ✓ | ✓ | ✓ | |
tenant:marketplace:offers:read | ✓ | ✓ | ✓ | |
tenant:marketplace:offers:write | ✓ | ✓ | ||
tenant:marketplace:offers:release | ✓ | ✓ | ||
tenant:mcp:invoke | ✓ | ✓ | ✓ | |
tenant:notifications:read | ✓ | ✓ | ||
tenant:notifications:read:self | ✓ | ✓ | ✓ | ✓ |
tenant:notifications:write:self | ✓ | ✓ | ✓ | |
tenant:notifications:audit:read | ✓ | ✓ |
Who Can Assign Each Role
| Role | Who can assign |
|---|---|
viewer | admin, owner (via invite or change_member_role) |
member | admin, owner |
admin | owner only |
owner | Cannot be directly assigned; only transferred from the current owner |
Owner Role Mechanics
One Owner Per Tenant
There is exactly one owner per tenant at all times. The owner role cannot be assigned directly — it can only be transferred from the current owner. The transfer is atomic and simultaneously:
- Changes the current owner’s membership role to
admin. - Changes the target member’s membership role to
owner.
This prevents any moment where zero owners exist.
Why the Owner Cannot Be Removed
DELETE /v1/tenants/{id}/members/{owner_sub} returns 409. The owner must transfer ownership before they can be removed from the tenant. This prevents accidental orphaning of a tenant.
Owner Is the Only Member Who Can Delete the Tenant
tenant:delete is held exclusively by the owner role. Soft-delete requires the owner’s JWT. The owner’s token must also pass the token revocation check (Cognito GetUser call) before the delete is processed, preventing a stale token from soft-deleting a tenant after the owner has signed out everywhere.
Invite Flow
Invites carry the target role at creation time. The creator must hold tenant:members:invite and the creator’s role must be at least as privileged as the invited role:
- An
admincan inviteadmin,member, orviewer. - An
ownercan invite any role (but notowner— transfers require the current owner).
An invite cannot create an owner directly. Post-Confirmation auto-accept creates the membership at the invited role; promotion to owner requires a subsequent transfer.