On-Call Access & Roles
The On-Call workspace is a full-screen area of the Console (its own left rail: Monitor, Configure, Migration, System). Historically every on-call page was super-admin only. The Foundation (P0) release brings it to Grafana OnCall's multi-user model: a responder sees the alert stream, a team on-call manager self-serves their own team's schedules and escalation, and super-admins are unaffected and still see everything.
This page explains the permission model, the three new permissions, how to make someone a team on-call manager, and how the navigation splits between responders and admins.
The permission model
Proxima's permissions are per-client (a role grants a permission on the clients a user can access). On-call adds one twist: schedules and escalation policies are team-owned, not client-owned. For those two resources the gate layers a team-membership check on top of the permission.
On-call resources have four ownership shapes, each with its own gate:
| Resource | Owned by | Who may write it |
|---|---|---|
| Alert groups | client | alerts:read to view, alerts:write to act (ack/silence) — per client |
| Escalation routes | client | escalation:write on the route's client (Can) |
| Integrations (alert sources) | client | existing alert_sources:read / alert_sources:write — reused, no new permission |
| Schedules (+ rotations & overrides) | team | IsSuperAdmin OR (member of the owning team AND schedules:write) |
| Escalation policies (steps) | team | IsSuperAdmin OR (member of the owning team AND escalation:write) |
| Personal notification prefs + phone | user | self-or-admin — the user themselves, or an admin with users:write |
| Notification Modes, Cutover, ChatOps config | global | IsSuperAdmin only |
Reads of team-owned resources require any on-call permission (or IsSuperAdmin); listing is scoped
to the teams the caller can see (see Read-scoping model).
For team-owned resources, the write gate is enforced in-handler on every mutation (never route-middleware alone — see the union-check pitfall in the RBAC standard). On each write the backend:
- Resolves the resource's owning team.
- Allows iff
IsSuperAdminOR (the caller is a member of that team AND holds the resource's:writepermission on any of their clients).
Anything else — an unconfirmed membership, a missing permission, or a lookup error — fails
closed (403).
The read-scoping model
Reads of the team-owned config surfaces (schedules and escalation policies) are tenant-scoped,
not super-admin-only. The rule is symmetric with the write gate but wider — any on-call permission
grants view, while a :write on your own team grants edit:
- A caller holding any on-call permission (
oncall:readORschedules:writeORescalation:write) on a client may view that client's teams' schedules and escalation policies — read-only. Visibility is resolved server-side: a team is visible when it is assigned (viateam_clients) to a client the caller can access with an on-call permission. - The caller may edit only their own teams (the teams they belong to while holding the
relevant
:write). Every other visible team renders read-only — its controls are present but disabled, and any mutation still403s at the backend gate. - Escalation routes are client-scoped: a caller sees the routes for each client they can access.
- A super-admin sees and edits everything.
- Cross-client data is never visible. The tenant boundary is enforced entirely in the backend:
the list endpoints (
ListSchedulesInClients/ListPoliciesInClients) jointeam_clientsto the caller's on-call-visible client set (onCallVisibleClients; an empty set returns an empty list without querying), and the detail/shifts/now endpoints resolve the resource's owning team and enforce an in-handler read gate (authorizeTeamRead). A hidden or disabled frontend control is advisory only — the backend is authoritative.
Writes are unchanged — they still go through P0's authorizeTeamWrite team-membership gate
described above (any on-call permission grants read; only :write on your own team grants edit).
The three new permissions
The Foundation release adds exactly three permission strings (format resource:action). A
migration grants all three to every role that already holds system:write, so today's on-call
admins keep their access when the workspace moves off system:write gating.
| Permission | Grants |
|---|---|
oncall:read | View on-call config surfaces (schedules, escalation, and — as later phases land — the users directory and insights). |
schedules:write | Create and edit on-call schedules, rotations, and overrides for teams you belong to. |
escalation:write | Create and edit escalation policies (team-owned) and escalation routes (client-scoped). |
Everything else on-call reuses existing permissions: alert groups use alerts:read / alerts:write,
integrations use alert_sources:read / alert_sources:write, and personal notification preferences
use the existing self-or-admin rule.
Capabilities on /auth/me
So the React rail can filter itself, /auth/me reports the caller's on-call capabilities:
oncall_read(bool) — the caller may view on-call config surfaces (holdsoncall:read,schedules:write, orescalation:writeanywhere, or is a super-admin).oncall_write_team_ids(string[]) — the team IDs the caller may write schedules/escalation for (their team memberships, when they hold a write permission). For a super-admin this is the sentinel["*"]meaning "all teams"; for a responder it is[].
Making someone a team on-call manager
To let a user self-serve their team's schedules and escalation without granting super-admin:
- Add them to the team (Admin → Teams → the team → Members). Team membership is the tenant
scope for team-owned writes — a user with
schedules:writewho is not a member of the team is still denied (403). - Grant the write permissions on their role:
schedules:writeand/orescalation:write(addoncall:readso they can view config even for teams they don't manage). Do this in Admin → Roles → their role.
Once both are in place, the user:
- sees the Configure group in the On-Call rail (Schedules, Escalation Chains), opens those pages, and sees their teams' config: their own teams are editable, every other visible team (any team on a client they can access) renders read-only,
- can create and edit their team's schedules and escalation policies (in the UI and via the API),
- is blocked (
403) editing another team's resources — the read-only affordance is advisory; the backend gate is authoritative, - and their
/auth/mereportsoncall_write_team_ids: [their-team-id].
They do not get Notification Modes or Cutover — those stay super-admin only.
Responder vs. admin: the navigation split
The On-Call rail is permission-filtered: each item renders only if the caller holds its gate, and a group with no visible items is dropped entirely. This is what turns the workspace from super-admin-only into the multi-user model.
| Rail group | Items | Visible to |
|---|---|---|
| Monitor | Alert Groups | anyone with alerts:read (responders included) |
| Monitor | Readiness | anyone who can view on-call config (canViewOnCall()) |
| Monitor | My On-Call | everyone — deliberately ungated; the page reads only the caller's own paging phone and notification chain, from self-readable endpoints |
| Configure | Escalation Chains, Schedules | anyone who can view on-call config (canViewOnCall() — holds any on-call permission) — their teams' config is editable, everyone else's renders read-only |
| Migration | Notification Modes, Cutover | super-admins only |
| System | Settings | super-admins only (the endpoint is global infra with no tenant axis, so anyone else would follow it into a 403) |
So in practice:
- A responder (
alerts:readonly) sees two Monitor items: Alert Groups and My On-Call. They have no Readiness, Configure, Migration or System entries, and any schedule/escalation/route mutation returns403. - A team on-call manager opens the Configure page UIs (Schedules, Escalation Chains) and sees
their teams' config: the read endpoints are tenant-scoped, so a manager sees the schedules and
escalation policies for every team on a client they can access (read-only), and edits only
their own teams. Editing another team's resources is blocked (
403) at the backend gate. - A super-admin sees everything, including Migration (Notification Modes, Cutover), and can edit any team.
The On-Call sidebar entry lands users monitor-first: anyone with alerts:read lands on Alert
Groups (/alerts), super-admins included. Only a caller without alerts:read falls through to
the first accessible /oncall/* config page, and finally to the ungated /oncall/me.
This is deliberate and replaced an earlier config-first rule. The sidebar entry that owns this href carries the live red firing-count badge — it is the one affordance in the product that says something is on fire, so it must open the fire, not the fire code. Grafana OnCall homes on Alert Groups and PagerDuty on Incidents; neither opens anyone on a policy editor. The decision is also static — it reads only the caller's capabilities, so the sidebar computes the href synchronously on first render and it never moves under the user.