Webhook payloads
Reference for every payload GreenSlope POSTs to an
outbound webhook. All payloads share a
common envelope; each kind adds its own body.
Envelope
Every request body looks like:
{
"event": {
"id": "evt_01HJ1…",
"kind": "alert.opened",
"created_at": "2026-04-21T14:02:18Z",
"tenant_id": "ten_01H…"
},
"alert": { "…": "see per-kind sections below" }
}event.idis unique per event and stable across retries. Dedupe on this.event.kinddecides which top-level fields are populated.created_atis RFC3339 UTC.
Signing
If you configured a signing secret when adding the webhook destination, every request includes:
x-greenslope-signature: <hex-encoded HMAC-SHA256 of the raw body>
x-greenslope-timestamp: <unix epoch seconds>Reject requests whose timestamp is more than 5 minutes from now — this is the replay-attack mitigation.
alert.opened
Fired when a new alert transitions from idle to open.
{
"event": { "id": "…", "kind": "alert.opened", "…": "…" },
"alert": {
"id": "alr_01HJ…",
"url": "https://app.greenslope.io/alerts/alr_01HJ…",
"title": "Burn rate 14.4x on web:availability",
"severity": "sev1",
"kind": "burn_rate",
"service": "web",
"environment": "production",
"slo": {
"id": "slo_01H…",
"name": "availability",
"burn_rate": 14.4,
"window_short": "5m",
"window_long": "1h"
},
"suspected_release": {
"id": "7fa9c21a…",
"pr": 842,
"pr_url": "https://github.com/…/pull/842",
"deployed_at": "2026-04-21T14:00:02Z"
}
}
}suspected_release is only present when release attribution has a
candidate. Absent otherwise.
alert.acknowledged
{
"event": { "id": "…", "kind": "alert.acknowledged", "…": "…" },
"alert": {
"id": "alr_01HJ…",
"acknowledged_by": {
"user_id": "usr_01H…",
"email": "ada@example.com"
},
"acknowledged_at": "2026-04-21T14:05:41Z"
}
}alert.resolved
{
"event": { "id": "…", "kind": "alert.resolved", "…": "…" },
"alert": {
"id": "alr_01HJ…",
"resolved_at": "2026-04-21T14:12:09Z",
"resolution": "auto" // or "manual"
}
}alert.escalated
Fired when a sev-1 alert hasn't been acknowledged within the escalation window and progresses to the next destination (e.g. SMS → voice).
{
"event": { "id": "…", "kind": "alert.escalated", "…": "…" },
"alert": {
"id": "alr_01HJ…",
"from_channel": "sms",
"to_channel": "voice",
"escalated_at": "2026-04-21T14:05:00Z"
}
}change_event.created
Fired when a new change event is recorded (from the REST API, Vercel webhook, or dashboard).
{
"event": { "id": "…", "kind": "change_event.created", "…": "…" },
"change": {
"id": "chg_01HJ…",
"kind": "deploy",
"service": "web",
"environment": "production",
"release_id": "7fa9c21a…",
"actor": "ada",
"pr": 842,
"pr_url": "https://github.com/…/pull/842",
"at": "2026-04-21T14:00:02Z"
}
}Retry semantics
2xx— delivered.5xx, timeout (>10 s), connection reset — retried with exponential backoff for up to 24 hours.4xx(except 408 Request Timeout) — permanent failure, sent to the dead-letter view.
Per-destination delivery state is visible in the dashboard at Settings → Alerts → Webhook destinations → Activity.
Related