Skip to content

Incident Alerting Platforms

Paging the on-call engineer is arguably the best-fitting category in this whole book: alerting platforms are built to ingest machine-generated events over plain HTTP, they all speak JSON, and — the headline — their APIs are designed idempotent, which upgrades pg_relay's delivery guarantee in a way email and SMS never can. Twelve platforms assessed, eleven fit, and the first adapter has shipped: PagerDuty (p_provider => 'pagerduty' — setup and usage in Resend, Telnyx, and PagerDuty) — the industry's reference ingestion API, and (not coincidentally) the API whose body-side routing_key motivated the body_merge feature that now serves this whole category. The other rows remain assessments — not yet selectable at create_profile(). (Worked wire-level profiles for all eleven: pg_relay's incident alerting chapter.)

The idea that makes this category special: dedup keys

pg_relay delivers at least once: a crash between a successful send and the commit re-sends the event. For email, the deterministic Message-ID merely mitigates that; for alerting platforms it disappears entirely, because their ingestion APIs deduplicate by design — PagerDuty's dedup_key, incident.io's deduplication_key, Splunk On-Call's entity_id. Derive that field deterministically from the notification's own primary key (pgrelay-{id}) and a redelivered event updates the alert it already created instead of paging twice. At-least-once becomes exactly-one-alert. Every adapter in this category will derive the dedup field this way — never randomly.

The same key drives the alert's lifecycle from the database side: closing an alert is just another outbound send — event_action: "resolve" (PagerDuty) with the same dedup key — so a second channel can resolve what the first one triggered. What never flows back is acknowledgement state: platforms report ack/resolve through their own outbound webhooks, and pg_relay has no inbound listener — the platform owns the human side.

The assessment

Platform Fits? Auth shape Notes
PagerDuty shipped adapter body_merge (routing_key) Events API v2; 202dedup_keyuse it
Jira Service Management Custom header (GenieKey …) Opsgenie's API relocated into JSM; per-integration key (not an account token); region-sensitive base URL; alias is the dedup key
incident.io Bearer Clean fit; HTTP alert-source URL carries the source id
Better Stack Bearer Clean fit
FireHydrant Bearer Clean fit (Signals or /v1/incidents)
Rootly Bearer JSON:API body shape; Content-Type: application/vnd.api+json via headers
Grafana Cloud IRM URL-keyed Whole integration URL is the credential (as _env:); OSS OnCall is archived — IRM only
Splunk On-Call URL-keyed entity_id deduplicates; RECOVERY on the same id resolves
Squadcast URL-keyed Must use its Incident Webhook API — its v3 API needs a token-exchange flow, which is out
xMatters Basic Event body depends on the receiving workflow — live-verify especially carefully
Runframe URL-keyed or Bearer Both patterns offered; webhook route is natural for alerts
OnPage ✘ excluded Login flow + callbacks Auth is a session-token flow, not a static credential, and its status model requires inbound callbacks — both structural, not recipe gaps

Classification is uniform across the table — 2xx → sent (id or dedup key as provider ref), 429/5xx/no-response → retry, other 4xx → failed — and unlike SMS's Infobip, nobody here hides failure inside a 200.

PagerDuty and body_merge — the motivating case

PagerDuty's Events API v2 authenticates with a routing_key inside the JSON body — no auth header at all. That's precisely what the profile's body_merge overlay exists for: the key lives in a Processor-host environment variable, the profile injects it at send time, and the notifier's message data never contains it. If a producer does supply a routing_key, the profile's value silently overwrites it — the key is profile-owned, by design, so a producer can neither spoof nor redirect the paging destination. The profile carries a placeholder auth block (the transport requires one; PagerDuty ignores it) plus:

"body_merge": {"routing_key": "_env:PD_ROUTING_KEY"}

The shipped adapter mirrors Slack's shape: p_provider => 'pagerduty', summary and severity from the notification (with p_payload carrying event-native extras — event_action, custom_details, component, and the rest), the dedup key derived from the notification id automatically (explicit for acknowledge/resolve, which must target the trigger's key), and PagerDuty's returned dedup_key recorded as the provider reference. create_profile() enforces the body_merge routing key up front. Full setup: Resend, Telnyx, and PagerDuty.

One honest caveat, inherited deliberately

The assessments on this page (and the SMS page) are documentation-verified, not live-verified — endpoints, auth mechanics, and response shapes come from each platform's current published documentation, the same standard pg_relay's book states for its recipes. That includes the shipped PagerDuty adapter itself: unlike Slack, it has not yet been proven with live sends — trigger one real event against a test service before relying on it in production, and treat any profile built ahead of an adapter as a design to be smoke-tested.

Which platform, if you're choosing fresh

If you already run one, the table answers whether it fits. If you're choosing: PagerDuty if you want the ecosystem default and the first shipped adapter here; incident.io / Better Stack / FireHydrant if you want the cleanest possible API surface (plain bearer + JSON); JSM Operations if your organisation is already Atlassian-shaped. The platforms whose only route is a URL-keyed ingestion endpoint work fine, but a revocable token beats a secret-bearing URL for credential hygiene — same reasoning as Mattermost's two routes.


Back to the provider overview, or the status table.