Permissions
The single reference for who can do what — human roles, agent scopes, per-resource visibility, and how they compose.
The model in one paragraph
Every action in BoardHerald passes up to three checks: (1) does the human role of the actor permit the action at all, (2) if the action came via an agent, does the API key scope include it, and (3) for resources with per-item visibility (today just financials), is this specific object visible to the actor? All three are enforced server-side. Failure on any one is a 403.
Layer 1: Human roles
Every user in a tenant holds exactly one role. The role sets the ceiling for everything that follows — an API key issued by a MEMBER can never exceed a MEMBER's reach, no matter which scopes are checked on it.
Roles are scoped per tenant. The same email can be an ADMIN in one organization and a MEMBER in another — each tenant tracks its own role for you, and switching organizations via the tenant switcherre-resolves your role from the destination. A role badge in one tenant carries zero authority into another.
Role definitions
| Role | Typical holder | Scope |
|---|---|---|
ADMIN | Founder, CEO, CFO, executive assistant | Full read + write on every surface. Can invite, promote, demote, rotate NDA, see audit. |
MEMBER | Board member, investor | Read all in-scope content, vote on resolutions, acknowledge updates, RSVP meetings. |
OBSERVER | Advisor, early investor with observer seat | Read-only. Cannot vote, doesn't count toward quorum. |
Human permissions matrix
Every major action, row by row, per role:
| Action | ADMIN | MEMBER | OBSERVER |
|---|---|---|---|
| Updates — read | ✓ | ✓ | ✓ |
| Updates — publish / edit | ✓ | — | — |
| Resolutions — read | ✓ | ✓ | ✓ |
| Resolutions — draft / open | ✓ | — | — |
| Resolutions — vote | ✓ | ✓ | — |
| Meetings — read / RSVP | ✓ | ✓ | ✓ |
| Meetings — schedule / minute | ✓ | — | — |
| Financials — read (visibility-gated) | ✓ | ✓ | ✓ |
| Financials — enter / restate | ✓ | — | — |
| Users — invite / promote / demote | ✓ | — | — |
| NDA — manage / rotate | ✓ | — | — |
| Audit log — read | ✓ | — | — |
| Branding / white-label | ✓ | — | — |
| API keys — issue / revoke | ✓ (own + tenant-wide) | self only | — |
Layer 2: Agent scopes
API keys carry a set of fine-grained scope strings. Every MCP tool declares a required scope and fails closed with a 403 if the key doesn't carry it. The scope check is independent of the human role check — both have to pass.
Naming convention
Scopes follow the shape <resource>:<action>. Resources are top-level domains (updates, meetings, financials, etc.); actions are one of read, write, or a resource-specific verb (vote for resolutions, manage for users).
Complete scope reference
Every scope the server recognises today. The tools column lists the MCP tool names that scope unlocks — see Agents → Capabilities for what each tool does.
| Scope | Tools covered | Notes |
|---|---|---|
audit:read | audit_list | Sensitive — exposes who-did-what across the whole tenant. Never bundled into built-in personas below full admin. |
categories:read | categories_list | Tenant-defined tag / category metadata used to organise updates and resolutions. |
financials:read | financials_list, financials_get, kpis_list, kpis_get, kpis_sparkline | Covers both financial updates (encrypted HTML reports) and the KPI surface. Respects per-KPI visibility (Layer 3 below). |
financials:write | financials_create, financials_publish, financials_update, financials_delete, kpis_create, kpis_update, kpis_delete, kpis_record_period, kpis_restate, kpis_target_create, kpis_target_update, kpis_target_delete | Author financials, record and restate KPI periods, manage time-varying targets. Role ceiling still applies — a MEMBER key with financials:write still can't write (not a MEMBER action). |
kpis:webhook | kpis_webhook_ingest | Narrow transport scope for sync connectors. POST a single period value per call via /api/kpis/webhook; identify the KPI by id or by (sourceSystem, externalId). Separate from financials:write so a sync key can't rewrite KPI metadata. See Agents → Sync KPIs. |
meetings:read | meetings_list, meetings_get | — |
meetings:write | meetings_create, meetings_publish, meetings_update, meetings_cancel | Schedule, amend, cancel. |
notifications:read | notifications_list | Agent-visible notification feed for the issuing human. |
notifications:write | notifications_mark_read | Mark items read on the human's behalf. |
resolutions:read | resolutions_list, resolutions_get | — |
resolutions:vote | resolutions_vote | Deliberately separate from write — an agent can vote on the human's behalf without being able to author new resolutions. |
resolutions:write | resolutions_create, resolutions_close, resolutions_update | — |
search:read | search_query | Full-text search across updates, resolutions, meetings. Results still filtered by the issuing human's visibility. |
updates:read | updates_list, updates_get | — |
updates:write | updates_create, updates_publish, updates_update, updates_delete | Draft, publish, amend, retract. |
users:read | users_list, users_get | List members and inspect their profiles. |
users:write | users_invite | Invite new members. Role ceiling applies — only ADMIN keys can meaningfully use this. |
users:manage | users_update, users_disable, users_grant_access, users_revoke_access | Role changes + access grants + disable. ADMIN-only in practice. Both grant/revoke tools take a group parameter accepting FINANCE or INVESTOR; users_invite also takes optional grantFinance + grantInvestor booleans. |
* | (every tool) | Admin escape. Bypasses every scope check. Use sparingly — if a key only needs to read updates, don't mint it with *. |
scopes array, which the server treats as full access for backwards compatibility. Re-issue these keys with explicit scopes when you can.Layer 3: Per-resource visibility
Some resources have an additional visibility layer on top of role + scope. Today this applies only to financials — individual KPIs carry a visibility setting independent of the base surface permission.
Financials — KPI visibility
Each KPI is tagged with one of three visibility classes (configured by an ADMIN on the KPI itself):
- Full-board — visible to every member who has
financials:readat the role level. MRR, runway, headcount typically live here. - Investor-only — visible only to members who hold the INVESTOR access group. Use for metrics the investor audience on the board should see but not the rest (ownership percentages, option pool remaining, fundraise-round size, and similar).
- Leadership-only — visible only to ADMINs plus an explicit allow-list of members. Individual comp breakdowns, multi-scenario runway projections.
Visibility is enforced server-side — a member who shouldn't see a KPI doesn't get it in the API response at all. Same for agents: a key with financials:read sees exactly the KPIs the issuing human can see, never more.
How roles and scopes combine
For an agent tool call to succeed, all of the following must be true:
- The human user who owns the API key has a role that permits the action (per the matrix above).
- The human has signed the current active NDA (see NDA).
- The API key carries the scope that the tool declares as required.
- If the target resource has per-item visibility (financials today), the human is on the visibility allow-list.
Failure on any step returns a 403 with a message identifying which check failed:
role_insufficient— the role can't do this, regardless of scope.nda_required— human hasn't signed the current NDA.scope_missing— role would permit it, but the key's scopes don't include the required one.visibility_denied— scope check passed, but this specific item isn't visible to this human.
See Agents → Troubleshooting for error-code specifics.
Built-in agent personas
For convenience, the API-keys UI offers four pre-composed persona bundles. They're scope lists, not roles — the role ceiling of the issuing human still applies. Pick the persona that matches the job; every persona is a subset of what an ADMIN human could do themselves.
See the full table with scopes on Agents → Capabilities. Summary:
read-only— brief/query only; can't write anything.meeting secretary— schedule, draft agendas, capture minutes.board secretary— drafts and routes resolutions, casts recorded votes, posts updates.full admin— full write surface plus user and audit visibility. Grant sparingly.
updates:read + financials:read" — or any combination — for a narrow-purpose agent.Managing permissions in practice
Promoting / demoting
Any ADMIN can change another user's role. The exception: demoting yourself requires a second ADMIN to confirm — the platform won't let the last remaining ADMIN demote themselves and leave the tenant locked. Every role change writes a role.changed event to the audit log.
Issuing an API key
Humans issue their own keys under Profile → API Keys. The UI surfaces the four personas plus a custom-scopes option. Key values show exactly once — store them in a secret manager.
Revoking
A human can revoke any key they own. An ADMIN can revoke any key in the tenant (Settings → API Keys). Revocation is immediate — in-flight tool calls using the revoked key fail with key_revoked on their next round trip.
Auditing
Every role change, key issuance, key revocation, and tool call is in the audit log with actor + timestamp + IP. Filter by action prefix role.* / key.* / tool.* to scope the view.