Skip to main content

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:

ResourceOwned byWho may write it
Alert groupsclientalerts:read to view, alerts:write to act (ack/silence) — per client
Escalation routesclientescalation:write on the route's client (Can)
Integrations (alert sources)clientexisting alert_sources:read / alert_sources:write — reused, no new permission
Schedules (+ rotations & overrides)teamIsSuperAdmin OR (member of the owning team AND schedules:write)
Escalation policies (steps)teamIsSuperAdmin OR (member of the owning team AND escalation:write)
Personal notification prefs + phoneuserself-or-admin — the user themselves, or an admin with users:write
Notification Modes, Cutover, ChatOps configglobalIsSuperAdmin 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).

Team-owned writes: the one new mechanism

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:

  1. Resolves the resource's owning team.
  2. Allows iff IsSuperAdmin OR (the caller is a member of that team AND holds the resource's :write permission 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:read OR schedules:write OR escalation: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 (via team_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 still 403s 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) join team_clients to 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.

PermissionGrants
oncall:readView on-call config surfaces (schedules, escalation, and — as later phases land — the users directory and insights).
schedules:writeCreate and edit on-call schedules, rotations, and overrides for teams you belong to.
escalation:writeCreate 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 (holds oncall:read, schedules:write, or escalation:write anywhere, 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:

  1. Add them to the team (Admin → Teams → the team → Members). Team membership is the tenant scope for team-owned writes — a user with schedules:write who is not a member of the team is still denied (403).
  2. Grant the write permissions on their role: schedules:write and/or escalation:write (add oncall:read so 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/me reports oncall_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 groupItemsVisible to
MonitorAlert Groupsanyone with alerts:read (responders included)
MonitorReadinessanyone who can view on-call config (canViewOnCall())
MonitorMy On-Calleveryone — deliberately ungated; the page reads only the caller's own paging phone and notification chain, from self-readable endpoints
ConfigureEscalation Chains, Schedulesanyone who can view on-call config (canViewOnCall() — holds any on-call permission) — their teams' config is editable, everyone else's renders read-only
MigrationNotification Modes, Cutoversuper-admins only
SystemSettingssuper-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:read only) 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 returns 403.
  • 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.