Surfacing PagerDuty On-Call on Entity Pages

The single most useful thing a portal can tell a responder is who to wake up. This how-to wires PagerDuty into Backstage so every service’s entity page shows its current on-call engineer and any open incidents, resolved through the service’s owner. It is the read-live-state half of Incident & On-Call Workflows within Developer Experience & Self-Service Platforms.

Prerequisites

  • Backstage 1.20+ with the catalog populated and service ownership accurate — the integration is only as correct as spec.owner.
  • A PagerDuty account with an API token that can read schedules and incidents.
  • Each service mapped to a PagerDuty service or integration key, so the portal knows which schedule to read.
  • The @pagerduty/backstage-plugin (frontend) and its backend module installed.
  • The API token injected at runtime as ${PAGERDUTY_TOKEN}, never committed.
Prerequisites for PagerDuty on entity pages Accurate ownership, an API token, per-service mapping, and the plugin enable on-call display. accurate ownership PagerDuty token per-service mapping plugin installed On-call shown on the entity
The mapping annotation is what ties a catalog entity to a PagerDuty schedule — without it the plugin has nothing to read.

Exact Configuration

  1. Annotate each service with its PagerDuty integration key so the plugin knows which schedule to read.

    # catalog-info.yaml
    apiVersion: backstage.io/v1alpha1
    kind: Component
    metadata:
      name: payments
      annotations:
        pagerduty.com/integration-key: ${PD_PAYMENTS_KEY}
    spec:
      type: service
      owner: team-payments
    
  2. Provide the API token to the backend module.

    # app-config.yaml
    # Requires @pagerduty/backstage-plugin-backend >= 0.5.0
    pagerduty:
      apiToken: ${PAGERDUTY_TOKEN}
    
  3. Add the card to the entity page so on-call and incidents render on the overview.

    // packages/app/src/components/catalog/EntityPage.tsx
    import { EntityPagerDutyCard } from '@pagerduty/backstage-plugin';
    
    const overviewContent = (
      <Grid item md={6}>
        <EntityPagerDutyCard />
      </Grid>
    );
    
Reading on-call onto the page The annotation identifies a schedule; the backend reads PagerDuty with the token; the card renders on-call and incidents. annotation integration key backend reads PD with token on-call + incidents current state entity card rendered
The token stays on the backend — the browser never sees it, so on-call data reaches the page without exposing the credential.

Validation

# Requires curl, jq
# 1. The backend can read PagerDuty at all
curl -s "${PORTAL_URL}/api/pagerduty/services" -H "Authorization: Bearer ${PORTAL_TOKEN}" \
  | jq '.services | length'
# Expected: > 0

# 2. A specific service resolves an on-call person
curl -s "${PORTAL_URL}/api/pagerduty/oncall?serviceId=${PD_SERVICE_ID}" | jq '.oncall[0].user.name'
# Expected: the engineer currently on call

# 3. Open incidents surface for the service
curl -s "${PORTAL_URL}/api/pagerduty/incidents?serviceId=${PD_SERVICE_ID}" | jq '[.incidents[] | select(.status=="triggered")] | length'
# Expected: the count of currently-triggered incidents
PagerDuty validation checks Backend connectivity, an on-call name for a service, and open-incident counts confirm the integration. Backend reads PD services > 0 On-call resolves a named engineer Incidents surface triggered count
If the backend lists services but a specific one resolves nobody, the fault is the per-service mapping, not the token.

Edge Cases & Troubleshooting

Symptom Root Cause Resolution
Card shows “no on-call” Annotation missing or wrong integration key Confirm pagerduty.com/integration-key matches a real PD service
Wrong person shown Service owner points at the wrong team Fix spec.owner; on-call resolves through ownership
401 from the backend API token invalid or lacks read scope Reissue a token with read access to schedules and incidents
On-call is stale during a handoff Response cached too long Lower the plugin’s cache TTL for on-call reads
Rate-limited under load Every page view hits the PD API Cache on the backend and share across viewers
PagerDuty pitfalls by class Missing annotations and wrong owners are mapping issues; token errors and rate limits are access issues. Mapping no on-call wrong person → fix key + owner Access 401 token rate limited → scope token + cache
Caching on the backend fixes both rate limits and staleness at once — one shared read serves every viewer of the page.

Frequently Asked Questions

Can responders acknowledge incidents from the portal?

Some plugin versions support acknowledge and escalate actions, but treat write access carefully: it needs a token with write scope and a clear audit trail. Many teams keep the portal read-only for incidents and drive acknowledgement from the paging tool, using the portal purely to shorten the “who and what” lookup.

How fresh is the on-call data?

As fresh as your cache TTL. On-call rarely changes mid-shift, so a short cache — a minute or two — is a good trade between freshness and API load. During a handoff you may briefly see the outgoing engineer; shortening the TTL narrows that window at the cost of more API calls.