Team, roles and permissions
How a workspace's members, invitations and seats work, the four workspace roles and the fourteen permissions of each workspace's roles matrix, what each seeded account sees, and how ownership moves.
For the Full Stack package
How access works
- An account is anyone who signs in. It belongs to a workspace through a membership with one role: Owner (exactly one per workspace), Admin, Member or Billing. The same account can hold a different role in each workspace it belongs to.
- Each workspace keeps its own roles matrix: which role holds which of fourteen permissions. The owner holds them all and cannot be edited, and two of them,
workspace.deleteandroles.manage, stay with the owner whatever the matrix says. A new workspace starts from the defaults below. - The API checks every workspace request on the server: an account that is not a member gets 404, as if the workspace did not exist, and a missing permission gets 403. A suspended workspace keeps every read and refuses every write.
- The dashboard hides the sidebar rows and buttons a role does not include, and a page opened by its address shows Access required.
- A change to a member's role or to the matrix applies to that member's next request to the API. Their sidebar and buttons follow the next time the dashboard reads the workspace, at the latest on a reload.
- Platform staff (Platform admin and Support) are a separate set of roles across every workspace: see Platform administration.
The seeded roles and accounts
Forma Studio, the seeded workspace, uses the default matrix. Each role has one seeded account that signs in to the dashboard; the installation guide lists their sign-in details.
| Role | Account | Permissions | Sidebar |
|---|---|---|---|
| Owner | owner@northstack.example (Alex Morgan) | All 14 | Overview, Projects, Team, Roles, Billing, Settings and AI assistant |
| Admin | admin@northstack.example (Sam Carter) | workspace.read, workspace.update, projects.read, projects.write, members.invite, members.remove, keys.manage, audit.read, ai.read, ai.apply | Overview, Projects, Team, Roles (read only), Settings with API keys, and AI assistant; no Billing |
| Member | member@northstack.example (Noor Hassan) | workspace.read, projects.read, projects.write, ai.read, ai.apply | Overview, Projects, Team, Roles (read only), Settings (read only for the workspace's details) and AI assistant |
| Billing | billing@northstack.example (Jordan Ellis) | workspace.read, billing.read, billing.manage | Overview, Team, Roles (read only), Billing and Settings; no Projects and no AI assistant |
workspace.readis what opens the Team, Roles and Settings rows, so every role, Billing included, sees the member list, the read-only matrix and the settings. The overview's projects, its weekly chart and the projects needing attention needprojects.read. Its recent activity and the bell show every event to a role holdingprojects.readoraudit.read; a role withbilling.readand neither of those (Billing, as seeded) sees only the billing events, and any other role sees none. The full activity log and its export needaudit.read.- The workspace's billing email comes back only to a role with
billing.readorbilling.manage. - Forma Studio has 8 members (an owner, an admin, five members and a billing contact) on the Pro plan's 10 seats, with two pending invitations and one archived project. The owner is also an admin of a second workspace, Monogram, which has its own owner, team, invitation and API key.
yarn seedwrites a workspace's matrix only when it has none, so running it again keeps the changes you made on the Roles page. Change every seeded password, or delete the sample accounts, before the dashboard is reachable from the internet.
Every workspace permission
The Roles page groups the permissions under the labels below. The list is defined once, in back-end/src/modules/workspaces/constants/workspace-permissions.ts, with the defaults every new workspace starts from.
| Group | Permission | On the Roles page | Default roles |
|---|---|---|---|
| Workspace | workspace.read | View workspace: the workspace and its team | Owner, Admin, Member, Billing |
| Workspace | workspace.update | Edit workspace: its details and notification switches | Owner, Admin |
| Workspace | workspace.delete | Delete workspace (owner only) | Owner |
| Projects | projects.read | Read projects: projects, tasks and files | Owner, Admin, Member |
| Projects | projects.write | Edit projects: create, edit, archive and restore projects and tasks, upload files | Owner, Admin, Member |
| Members | members.invite | Invite members: send, resend and revoke invitations | Owner, Admin |
| Members | members.remove | Remove members | Owner, Admin |
| Members | roles.manage | Manage roles: change a member's role and the matrix (owner only) | Owner |
| Billing | billing.read | View invoices: the plan and invoices | Owner, Billing |
| Billing | billing.manage | Manage subscription: the plan, the payment details and the billing email | Owner, Billing |
| Developer | keys.manage | Manage API keys | Owner, Admin |
| Developer | audit.read | Read activity log: read and export it | Owner, Admin |
| AI assistant | ai.read | Use the assistant | Owner, Admin, Member |
| AI assistant | ai.apply | Apply suggestions the assistant makes | Owner, Admin, Member |
What the assistant can reach
ai.read opens the AI assistant and grants nothing else. The assistant is offered only the tools the member's role allows, each needing the same permission as its page: without billing.read it cannot read the plan or the invoices, and without audit.read it cannot read the activity. It writes nothing by itself: applying the tasks it proposes needs both ai.apply and projects.write, checked again when Add is pressed.
The Team page
/en/dashboard/team has two tabs, Members and Invitations, each with a search and a Role filter. Members are listed owner first, then by role and by when they joined.
| Action | Where | Needs |
|---|---|---|
| Invite member | The page header | members.invite |
| Change role (Admin, Member or Billing) | A member's actions menu | roles.manage, so the owner |
| Make owner | A member's actions menu | The owner, with their password |
| Remove from workspace | A member's actions menu | members.remove |
| Resend invitation and Revoke invitation | An invitation's actions menu | members.invite |
- The owner's row has no actions: ownership moves only through Make owner.
- Removing someone takes them out of this workspace at once; their projects and tasks stay. Nobody removes themselves from this menu, and an admin may remove members and billing contacts but not another admin (the API answers 403).
- A removed member keeps their account and their other workspaces.
Invitations and seats
- Invite a member asks for an email address and a role, and lists what that role will be able to do. The person is mailed a link to
/en/invitations/<token>that lasts seven days; withoutSMTP_HOSTthe message, link included, is written to the API's log. - The invitation page shows the workspace, the role, the invited email (masked) and who sent it. Accepting needs the invited email signed in: someone new creates an account with that email first. An invitation for another email, an expired one or a revoked one is refused with its own message.
- Each plan has a number of seats: 3 on Starter, 10 on Pro and 30 on Scale. A pending invitation uses no seat; accepting one does. With no free seat, a new invitation is refused ("There is no free seat. Change the plan to invite more people."), and so is accepting one, checked in the same transaction so two people cannot take the last seat at once.
- Resend invitation makes a new link and a new seven-day expiry, once a minute at most and five times per invitation. Revoke invitation makes the link stop working.
- Inviting someone who is already a member, or who already has a pending invitation, is refused. Once a past-due workspace's grace period (
BILLING_GRACE_DAYS) has run out, new invitations are refused until it is paid.
The Roles page
/en/dashboard/roles shows the workspace's matrix: one row per permission, in the six groups above, and one column per role, each cell Allowed or Not allowed. The Owner column is locked, and the Delete workspace and Manage roles rows say Owner only.
- Only the owner changes it. Everyone else who holds
workspace.readsees it read only: "Only the workspace owner changes what each role can do." - Save changes first shows what each role gains and loses, then saves the whole matrix and writes one
roles.updatedevent to the activity log. Reset goes back to the saved matrix. - The four roles are fixed: a workspace cannot add a fifth. To give someone less than a role, take the permission away from that role, which changes it for everyone who holds it.
Ownership
- Make owner asks for the owner's password, then makes the chosen member the owner and the previous owner an admin, in one step. It is written to the activity log as
ownership.transferred. - The owner alone deletes the workspace, from Settings, with its name and their password.
- On the platform, an account that owns a workspace cannot be deleted until another member is made owner.
The routes behind these pages
| Route | Permission |
|---|---|
GET /api/workspaces/:workspaceId/members | workspace.read |
PATCH /api/workspaces/:workspaceId/members/:memberId | roles.manage |
DELETE /api/workspaces/:workspaceId/members/:memberId | members.remove |
POST /api/workspaces/:workspaceId/members/transfer-ownership | the owner |
GET /api/workspaces/:workspaceId/invitations | workspace.read |
POST /api/workspaces/:workspaceId/invitations, POST .../invitations/:id/resend, DELETE .../invitations/:id | members.invite |
GET /api/invitations/:token and POST /api/invitations/:token/accept | none to read; signed in as the invited email to accept |
GET /api/workspaces/:workspaceId/roles | workspace.read |
PUT /api/workspaces/:workspaceId/roles | the owner (roles.manage) |
:workspaceId is the workspace's public id, such as org_forma. Errors come back as codes: seat_limit_reached, already_member, already_invited, resend_too_soon, resend_limit_reached, owner_protected, workspace_owner_only and invalid_password among them.
How the permission checks are built
Included with your purchase. Sign in to read, or open it in your download.
Where the API and the dashboard check a workspace permission, and how to add one.