Skip to content

Alert webhook reference

What Cloud Viewer sends to an alert target. To set one up, see Send alerts where you work.

Targets are set up in the portal under Alerts → Delivery targets, by an admin. Pro delivers to one target, Team to up to five.

Field Target Notes
Name webhook, PagerDuty up to 60 characters, unique in the team. Shown on the page, in alerts about the target and in the audit log; empty, the address is shown instead
Send alerts to this URL webhook https:// only, up to 500 characters, no user name or password. Stored encrypted and never shown again: the portal shows only the scheme and host, because for many receivers the path is the credential. To change it, edit the target and paste the full URL again
Which alerts webhook, PagerDuty Warnings and critical alerts or Critical alerts only, per target. Push notifications are not affected
What we send webhook Standard payload or Coding-agent brief (see below)
Authentication header (optional), Header value webhook a header your endpoint expects, for example Authorization. The value is stored encrypted and never shown again. When editing, the field is Replace the header value: empty keeps the stored value. Clearing the name deletes the value. Send a test event sends the header too
Service region PagerDuty United States or European Union
Integration routing key PagerDuty from the Events API v2 integration you create in PagerDuty. Stored encrypted and never shown again. Cloud Viewer cannot issue a new one, because PagerDuty does. When editing, the field is Replace the routing key: paste a new key, or leave it empty to keep the stored one

Different targets can receive different alerts: for example PagerDuty for the on-call rotation, all warnings to an engineering channel and critical alerts only to an agent endpoint. Remove stops a target; adding it again later does not resend earlier alerts. A plan change never deletes a target: if the new plan allows fewer, the oldest keep delivering and the others are kept, paused, until the plan allows them again or you remove an older one.

A webhook target receives one POST request each time an alert starts or is resolved, and when you use Send a test event.

Header Content
Content-Type application/json
User-Agent CloudViewer-Webhook/1
X-CloudViewer-Event the event value, so you can route without parsing the body
X-CloudViewer-Delivery an ID that stays the same across retries; store it if you need idempotency. It equals the body’s id
X-CloudViewer-Signature sha256=<hex>, an HMAC-SHA256 of the raw body
your own header only if you set Authentication header (optional) on the target

Your own header cannot be one Cloud Viewer sets: Content-Type, Content-Length, User-Agent, Host, Transfer-Encoding or any name starting with X-CloudViewer-.

The body is UTF-8 JSON, sent compact (no whitespace) with its keys sorted. The example below is formatted for reading.

Every event and every receiver gets the same JSON structure:

{
"version": 1,
"event": "alert.firing",
"id": "9f2c…",
"sent_at": "2026-08-16T09:12:33Z",
"team": { "id": 42, "name": "Ops", "slug": "ops" },
"alert": {
"fingerprint": "a1b2c3…",
"rule": "disk_space_critical",
"severity": "critical",
"status": "firing",
"summary": "Disk almost full",
"description": "Disk 96% full on web-1 (/var)",
"started_at": "2026-08-16T09:11:02Z",
"resolved_at": null
},
"resource": { "id": "srv_1", "project_id": 10, "server_id": 7 },
"links": {
"app": "cloudviewer://alert/a1b2c3…?resource=srv_1&project=10&team=42",
"portal": "https://account.cloudviewer.app/a/ops/alerts/"
}
}
Field Meaning
version the schema version, 1. Added fields do not change it; it changes only for a change a receiver could break on
event alert.firing, alert.resolved or test
id the delivery ID, the same as X-CloudViewer-Delivery
sent_at when this request was sent, UTC. It is inside the signed body
team the team’s numeric ID, name and slug
alert.fingerprint the alert’s identity. With started_at it identifies one alert episode
alert.rule the rule key, for example disk_space_critical. See Alert rules
alert.severity warning or critical
alert.status firing or resolved
alert.summary, alert.description the rule’s text for this alert
alert.started_at when the alert started, UTC
alert.resolved_at null unless the event is alert.resolved
resource.id the resource the alert is on, or an empty string
resource.project_id, resource.server_id the connected project and monitored server, or null
links.app the same deep link the push notification uses. Opening it on a phone shows the alert in the app, where you can mute it
links.portal the team’s Alerts page in the portal

An alert that starts and resolves before it was delivered is sent once, as alert.resolved, with both timestamps.

The test event has the same shape: alert.rule is webhook_test, alert.severity is warning, alert.summary is “Cloud Viewer webhook test”, resource.id and links.app are empty, and project_id and server_id are null. Its id is based on the time, so it never collides with a real alert.

  • One request per target per event, as soon as the alert changes: no grouping and no delay. Each target gets its own copy, so one target being down does not affect the others.
  • Warnings and critical alerts only. Informational alerts stay in the app. A target set to Critical alerts only receives no warnings. Muted alerts are never sent.
  • Accepted means any 2xx response. The response body is read up to 2 KiB and discarded.
  • Timeout: 5 seconds for the connection and the response.
  • Redirects are not followed. A 3xx response counts as a failure.
  • Retry: once a minute. A failed delivery is tried again on the next minute, with the same delivery ID, until it succeeds or the target is switched off. A change older than six hours is no longer sent. After a failure, the remaining events for that target wait for the next minute.
  • Switched off after 20 failed attempts in a row, about twenty minutes. A successful delivery resets the count. Switch it back on on the Alerts page, or saving the target, turns it on again.
  • At most 20 events per team per minute. The rest follow in the next minute.
  • Public https addresses only. A URL must start with https:// and must not contain a user name or password. A URL that resolves to a private or internal address is refused when you save it and again before every request.
  • A new target does not receive older alerts. Delivery starts with the alerts that change after you add it.

The secret is shown once: when the target is created, and each time you use Issue a new signing secret. Editing a target keeps its secret. Compute the HMAC over the raw request body, before parsing the JSON, and compare in constant time:

import hashlib, hmac
def verify(raw_body: bytes, header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)

sent_at is part of the signed body, so you can also reject requests older than, for example, five minutes.

A webhook target can send the coding-agent brief instead of the standard payload: choose Coding-agent brief under What we send. The request is the same object with the same signature and version, plus one agent object:

"agent": {
"preamble": "You are being handed a production alert from Cloud Viewer …",
"brief_format": "markdown",
"brief": "# Incident brief — disk_space_critical on srv_1\n\n…"
}
Field Meaning
preamble your team’s Notes for alert hand-offs from Agent Hand-off Notes in the portal, or Cloud Viewer’s default text when the team has none
brief_format markdown
brief a short incident brief in Markdown: a title, an Alert section (rule, severity, status, times, summary, description cut to 500 characters), a Resource section and a Links section. A section with nothing in it is left out, not filled with “none”

The brief is written only from what the standard object contains (the alert, the resource, the links), so it discloses nothing extra. It is shorter than the brief the app produces: recent changes, metric trends and cost information are only available in the app, and the webhook brief leaves them out. The test event carries the agent object too, so you can see what your agent will receive. For a chat bridge, use the standard payload. More on briefs: Hand off to a coding agent.

The payload is generic JSON. It is not in Slack’s, Discord’s or Teams’ message format. If you point an incoming webhook URL from one of these services directly at Cloud Viewer, the message will not display properly, because each service expects its own format.

Put a small bridge in between: a Cloudflare Worker, a Lambda function, an n8n or Make step, or a few lines of code in something you already run. The bridge receives the JSON object, verifies the signature and posts the format your chat service expects.

If you send to your own endpoint (an incident tool, a script, a queue), no bridge is needed. The object above is the complete format.

A PagerDuty target (Team) sends PagerDuty Events API v2 events to the region you chose:

Region Endpoint
United States https://events.pagerduty.com/v2/enqueue
European Union https://events.eu.pagerduty.com/v2/enqueue

The routing key is in the body, so there is no signature and no X-CloudViewer-* header. The timeout, retry and switch-off rules are the same as for a webhook. Any 2xx counts as accepted.

Cloud Viewer PagerDuty event
alert.firing event_action: "trigger"
alert.resolved event_action: "resolve", with only routing_key, event_action and dedup_key
one alert episode one dedup_key: cv- and 32 hex characters, a hash of the team, the target, the alert’s fingerprint and its start time. Trigger and resolve carry the same key, so the resolve closes the incident the trigger opened
alert.summary payload.summary, cut to 1,024 characters (the rule key when there is no summary)
alert.severity payload.severity: critical or warning
resource.id payload.source; server <id> when there is no resource, the team slug when there is neither
alert.started_at payload.timestamp, the alert’s own start, not the send time
links.app links: [{"href": …, "text": "Open in Cloud Viewer"}]
links.portal client_url, with client: "Cloud Viewer"
rule, fingerprint, IDs, description payload.custom_details: rule, fingerprint, project_id, server_id, resource_id, description

An alert that flaps eleven times creates eleven incidents. To merge them, use PagerDuty’s own suppression rules. Two PagerDuty targets open separate incidents.

Send a test event sends a trigger with the summary “Cloud Viewer PagerDuty test — no alert is firing”, severity warning and a dedup key starting with cv-test-, then a resolve for it. It cannot resolve a real incident.