Roles & Permissions
VERA has four permission roles. Each role scopes what data is visible and what actions are allowed.
This page covers Action × Role — what each role can do. For a Field × Role breakdown — which specific dollar figures, rates, or other sensitive fields a role can see, and exactly where that is enforced in code — see Field-Level Data Visibility.
The four roles
Owner
Full access. Owners see everything including dollar amounts, all projects, all users, rates, and liabilities.
Owners can:
- Create, rename, and delete projects
- Set project budgets and deadlines
- Assign project managers
- Add, update, deactivate, and reactivate users
- Promote users to Manager or Owner
- Set hourly cost rates (Rate History)
- Create and cancel liabilities
- View all financial data (revenue, cost, margin, contract values)
- See all projects across all clients
- Approve or reject time off requests
- Log their own time off directly — no approval flow required
- Access all tools without restriction
Admin
Owner-level access, with one deliberate difference: an Admin is not counted in team statistics. Admins are the people who run the agency's operations — the kind of person who owns or oversees the system but does not personally deliver billable client work. Giving them the same visibility as an Owner is right; counting them in utilization or capacity is not, because they would drag every team-wide number down with hours they were never expected to bill.
Admins can:
- Everything an Owner can — full financial visibility (dollar amounts, rates, contract values, margins), all projects, all users, deadlines, liabilities, and every tool without restriction
- Still log their own time and hold allocation envelopes — their individual records are untouched
Admins are excluded from:
- Every team-delivery statistic — team availability, capacity forecasts, utilization, and any "team of N" denominator. If a team of 11 includes one Admin, the rollups are computed over the 10 people who actually deliver work, so the Admin's presence never dilutes the numbers.
Privacy mode is the one console control only an Admin has. It blurs every person's name, client name, project and proposal name, the workspace name, email addresses and Slack IDs on every console page, and blurs individual pay (the Rates and Direct labor tables, each person's cost rate, and a project's or proposal's cost breakdown), so a workspace can be shown without showing who it belongs to. The names stay on the page, blurred, so it is always plain that the mode is on. While it is on the console is view-only: every save is refused, with a warning on the page that it was not saved, and the CSV export is unavailable. It is switched from the sidebar without reloading the page, and the switch reading On is the only standing sign that it is on. It turns itself off after 12 hours. Free text, such as notes, is not searched beyond the names VERA already knows.
Apart from that switch, this is the only way an Admin differs from an Owner. The stored role stays admin (it is never silently converted to owner), which is exactly what keeps the statistics exclusion working.
Manager
Scoped access. Managers see their assigned projects and percentages — never dollar amounts. They are the day-to-day operational role.
Managers can:
- View their assigned projects (where they are the PM)
- See percentages: budget % used, margin %, utilization %
- See project deadlines (needed for delivery planning)
- Set the deadline on projects where they are the assigned PM
- Create and manage envelopes for their assigned projects
- Enable/disable tasks and set task budgets for their assigned projects
- Approve or reject time off requests
- Log sick days and leave on behalf of team members
- View whole-team capacity and availability (hours only — no cost or billing figures); project burn rate for assigned projects
- See monthly hours by project and by person, hours only, on Staffing → Hours, with a CSV download (issue 2698)
- See liabilities for their assigned projects (read only)
- See a project's Liabilities card on any project they are PM of record for (or a retainer period they are PM of) — the same liabilities Staffing → Liabilities shows them (issue 2618) — and add and cancel liabilities there in the web console; editing a liability is owner-only (issue 2693)
- See the LER ratio (never the underlying revenue or cost) for projects they PM, in the sidebar and on the project's own page (issue #2092)
- Open a team member's detail page (
/console/people/:id) for envelope-shaping and capacity context — with no pay, employment type, discipline, or email shown for that person (issue #2095) - Create projects and clients in the web console, including a new project's contract value and billing rate; once the project exists, those figures are shown only to owners (issue #2330)
- Make themselves the PM of a project that has no PM, with Make me the PM on the project's page (issue #2330)
Managers cannot:
- Delete projects, or rename, archive or delete clients
- Create projects or clients from Slack (the web console only)
- Change the PM of a project that already has one
- Set project-level budgets (Tier 1) — only Owners do this
- Write
contract_valueorbilling_rateviaupdate_project— these fields are owner-only on write (mirrors the read-side isolation). Attempting to update them returns❌ Failed: Only owners can change a project's …. A manager can writehours_budgetonly on a project they are PM of record for, from the web console - Promote users or change roles
- See dollar amounts, rates, or contract values
- Access projects they are not assigned to as PM
- Create or cancel liabilities on a project they are not PM of record for, or edit any liability
User
The base role. Users track their own time and manage their own schedule.
Users can:
- Log time for themselves
- Edit and delete their own time entries
- View their own time history
- See their own envelopes ("what should I work on?")
- Request time off (PTO, sick, leave)
- View their own time off history
Users cannot:
- Log time for others (nobody does; a PM can correct entries on their project)
- View other people's time entries
- See project deadlines (deadlines are never shown to Users under any circumstances)
- See financial data of any kind
- Manage envelopes, projects, or other users
- Approve time off requests
Console access: Users now have a console surface at /console/time, scoped to logging, viewing, editing, and deleting their own time entries. This is the same permission boundary as Slack (own entries only) — just a second surface for the same capability. All other /console/* routes remain manager+/owner-only for the user role. vendor users have no console access: VERA refuses to sign a vendor in, even when their record has an email address (issue 2617).
Permission matrix
| Action | Owner | Manager | User |
|---|---|---|---|
| Log time (self) | ✅ | ✅ | ✅ |
| Log time (others) | ✅ | ✅ (own projects) | ❌ |
| View own time | ✅ | ✅ | ✅ |
| View team time | ✅ | ✅ (own projects) | ❌ |
| Create project | ✅ | ✅ (web console only) | ❌ |
| Create client | ✅ | ✅ (web console only) | ❌ |
| Delete project | ✅ | ❌ | ❌ |
| Set project budget (Tier 1) | ✅ | ❌ | ❌ |
| Allocate hours (Tier 2) | ✅ | ✅ (own projects) | ❌ |
| Set task budgets | ✅ | ✅ (own projects) | ❌ |
| Assign PM | ✅ | ✅ (themselves, on a project with no PM) | ❌ |
| Add users | ✅ | ❌ | ❌ |
| Change user roles | ✅ | ❌ | ❌ |
| Deactivate/reactivate users | ✅ | ❌ | ❌ |
| See project deadlines | ✅ | ✅ | ❌ |
| Set project deadline | ✅ | ✅ (PM of project only) | ❌ |
| See dollar amounts / revenue | ✅ | ❌ | ❌ |
| See budget percentages | ✅ | ✅ (own projects) | ❌ |
| See rates (hourly cost) | ✅ | ❌ | ❌ |
| See contract values | ✅ | ❌ | ❌ |
| Set contract_value / billing_rate | ✅ | ❌ | ❌ |
| Set hours_budget | ✅ | PM of record only (console) | ❌ |
| Create liabilities | ✅ | ✅ (console, projects they PM) | ❌ |
| View liabilities | ✅ | ✅ (own projects) | ❌ |
| Cancel liabilities | ✅ | ✅ (console, projects they PM) | ❌ |
| Approve time off | ✅ | ✅ | ❌ |
| Request time off (own) | ✅ direct | ✅ pending | ✅ pending |
| Add company holidays | ✅ | ❌ | ❌ |
| View capacity forecast | ✅ | ✅ (hours only) | ❌ |
Admin holds the same permissions as Owner in every row above (owner-level access). The single difference is not an action permission but a statistics one: an Admin is excluded from every team-delivery rollup — team availability, capacity forecasts, utilization, and any "team of N" denominator — alongside vendors, who sell deliverables rather than time. An Admin can still log their own time and hold allocation envelopes; only the aggregate team numbers leave them out.
Role assignment
Roles are set in the 03. Users sheet:
"owner"— full access"admin"— owner-level access, excluded from team statistics"manager"— project-scoped access"user"— self-only access
Users are auto-registered with the user role when they first message the bot. Only Owners and Admins can promote a user to Manager, Owner, or Admin, and promotion is done in the web console on the Users page.
Changing your own role
Nobody can change their own role, with one exception: an Owner or an Admin can move their own account between those two roles, in either direction, at any time. They do it on the Users page, in their own row, where the role list offers only those two values.
The swap is safe in a way no other self-change is. An Admin sees and does everything an Owner does, so the account keeps every permission it had and nobody can lock themselves out. Moving yourself down to Manager or User would strand the account, so it stays unavailable — as does changing your own role at all if you are a Manager or a User.
What does change is how you are counted. An Admin is treated as an operator rather than someone who delivers work, so an Admin is left out of capacity, availability, utilization, and every team total. Becoming an Admin therefore moves your own workspace's team figures; becoming an Owner again moves them back.
A workspace must always keep at least one person with owner-level access. Because Admins have that access, the last Owner may become an Admin — the workspace still has someone who can run it. What is refused is removing owner-level access from the last person who holds it.
Promoting via assign_pm
The assign_pm tool is a shortcut that promotes a User to Manager and assigns them to a project in a single step. PM assignment happens in the web console, on the project's page. Someone can be made PM before they have ever messaged the bot — the console identifies people by their record, not by a Slack ID.
Employment type restricts which role a vendor may hold
A person's employment type — full-time employee (FTE), contractor, or vendor — is a separate field from their permission role, and for FTEs and contractors the two are fully independent: a contractor may hold any role, including Manager, Owner, or Admin, exactly like an FTE. A contractor PM or a contractor operations lead is ordinary agency staffing.
A vendor is different: vendors don't use VERA at all (nobody records time for them, and they're paid through a fixed payment agreement rather than an hourly rate), so a vendor can only hold the User role — a vendor record with any other role would be a login-less account still holding permissions. A vendor record can never carry a Slack ID, for the same reason. It may carry an email address for reference, but the console refuses to sign a vendor in.
This is enforced wherever a user is created or edited, on every surface — the web console's Add-user modal and the underlying tools alike — not just hidden in one form. Changing an existing person's employment type to vendor is blocked the same way if their current role wouldn't be valid for it.
Deadlines and Users
Deadlines are visible to Owners and Managers. They are never shown to Users under any circumstances. This is a deliberate design decision — deadlines exist for financial accrual and capacity planning, not for individual task management. When a User asks about a project deadline, VERA omits it from the response without acknowledging that a deadline exists.
Financial data in public spaces
Financial figures — hourly rates, contract values, agreed liability amounts — must never appear in shared or public Slack channels (including the audit channel). The audit channel is visible to anyone subscribed, which may include Users who are not permitted to see financial data.
VERA enforces this in two ways:
set_ratedoes not post to the audit channel at all — rate changes are recorded in the server console only, never in Slack.create_liabilityposts an audit message recording the action (vendor, project, description) but never includes the dollar amount. The actual figures are only accessible to Owners via direct conversation with the bot.
Identifying who is asking
Every request names the person making it, and who that is decides what the answer contains — which projects are visible, whether a dollar figure is included, whose time is being logged.
There are two ways a request can name someone, and they are not interchangeable:
- From Slack, the person is named by their Slack ID. This is the only identity Slack has.
- From the web console, the person is named by their own account. Console access is by email — you sign in with Google or Microsoft, and that email is matched to your user record — so a Slack ID is not required to use the console. Someone onboarded by email who has never been connected to Slack works normally: they see their own data, log time that lands on their own record, and are held to their own role.
Where both are present, the console account wins. It is the more precise of the two, and it is the one the person actually signed in with.
A request that names nobody is refused with "User not found in system." VERA does not fall back to a best guess, and it does not treat a missing name as "any person without one." This holds in both directions: a blank Slack ID names nobody, and so does a console account that matches no user record — the second is never quietly retried as the first. A request that cannot say who it is on behalf of gets no answer at all.
This matters because a person's record can legitimately exist without a Slack ID. Where several such people exist in the same workspace, a blank Slack ID is genuinely ambiguous between them, and answering it would mean picking one arbitrarily — confidently returning another person's rates, or filing one person's hours against someone else's name. Refusing is the only correct response, and naming the person by their console account is how that ambiguity is avoided rather than merely refused.
One thing the console can do that Slack cannot: name whose behalf a request acts on. VERA's assistant has no way to do this at all — it is not part of the tool surface the model can reach — so the model can only ever act as the person it is talking to.
Inactive users
Deactivated users (status = "inactive") cannot log time or take any actions. Their historical entries are preserved. They appear in list_users with (inactive) next to their name.
Use reactivate_user to restore access.