Team & roles
Five roles. Each one sees a different slice of the app. Pick the smallest role that lets the person do their job.
The five roles
Owner. Full access including billing, ownership transfer, and workspace deletion. There's always at least one Owner; you can't demote the last one without promoting someone else first.
Admin. Everything an Owner can do except billing and ownership. Use for trusted operators who run the day-to-day but shouldn't see the credit card.
Accountant. Read-only access to everything including costs, payments, and reports. Use for your bookkeeper. They can't break anything but they can see the books.
Fulfillment. Tightly scoped to the warehouse loop: scans boxes, runs cycle counts, sees on-hand quantities. Does not see costs, payments, reports, suppliers, POs, write-offs, assets, or sales. Use for your warehouse team — they get a clean, distraction-free view focused on operations.
Viewer. Read-only inventory views. No costs, no money. Use for stakeholders who need visibility without interacting.
What each role sees
Money columns (landed cost, line totals, COGS, payments) are gated by the costs:view permission. Owner, Admin, and Accountant see them. Fulfillment and Viewer never do — including in tables, reports, and the dashboard's Total inventory value KPI.
Sidebar items are filtered by role. A Fulfillment user doesn't see "Reports" or "Suppliers" in their nav at all. URL bypass is closed too: typing /reports in the address bar redirects them to /dashboard.
Adding teammates
Open Team (Owner/Admin only). Add member asks for email, name, role, and a temporary password. The new member must:
1. Sign in with the temporary password you set 2. Get redirected to /change-password 3. Set their own password before they can use the app
This mustChangePassword flag is only used at admin-add time. The forgot-password flow doesn't set it.
Changing roles
On the Team page, click a member's row to change their role or remove them. Role changes take effect on the member's next page load (within a request, not realtime — sessions refresh on every navigation).
You can't demote or remove the last Owner. Promote someone else first if the current Owner is leaving.
Cross-workspace memberships
A single user (one email + password) can belong to multiple workspaces with different roles in each. Useful for bookkeepers who serve multiple clients. The user gets dropped into the workspace of their oldest membership on sign-in; switching workspaces in the UI is on the roadmap.
Permissions reference
The full permission list lives in lib/auth/permissions.ts. Twenty-five fine-grained permissions are bundled into the five roles above. Both server actions (requirePerm) and UI gates (can) read from the same source of truth, so a permission added once flows everywhere.