Subscriber webhooks vs. operator webhooksSubscriber webhooks are created by visitors on the public page and only
carry incident and maintenance updates for that page. Operator webhooks are
created on the Alerts page as a Generic webhook integration, carry
more event types, and use a different body. See the
webhook payload reference for those.
Both use the same signature scheme.
Request
Every delivery is an HTTPSPOST with a JSON body.
Every subscriber webhook has a signing secret, so the signature headers are
always present.
Events
There is no separate event when a maintenance window starts:
maintenance.starting_soon covers it. Incident edits that are not a posted
update, deletions, and anything on a draft send nothing. When an update
closes an incident (a Resolved update), only incident.resolved (or
maintenance.completed) is sent for it, not an additional
incident.message_added.
The subscriber notification events
reference explains which subscribers receive each event.
Payloads
Updates
Every incident and maintenance event uses the same shape.incident.published
incident.message_added
maintenance.scheduled
incident.resolved, maintenance.starting_soon, maintenance.completed,
and maintenance.canceled have the same fields; only event, headline,
and the content differ.
Test message
Sent when the webhook is connected. The subscription is created only if your endpoint answers this request with a 2xx status.subscription.connected
event_id or incident_id. It is signed like every other
delivery.
Verify the signature
The signing secret is shown once, in the subscribe form, when the webhook is connected. It starts withwhsec_. Use the whole string, prefix included, as
the HMAC key.
To verify a delivery:
- Read
X-Observer-Timestamp. Reject the delivery if it is more than five minutes from your server’s clock. This bounds replay of a captured request. - Compute HMAC-SHA256 with the secret as the key over
${timestamp}.${rawBody}: the timestamp header value, a period, and the request body exactly as received. Hex-encode the digest and prependsha256=. - Compare the result with
X-Observer-Signatureusing a constant-time comparison. Reject the delivery if they differ.
- Node.js
- Python
server.js
Delivery behaviour
- Timeout: your endpoint must answer within 10 seconds.
- Success: any 2xx status. The response body is ignored.
- Failure: any other status, a timeout, a network error, or a redirect. Redirects are never followed; configure the final URL.
- No retries: each event is sent once per subscription. A failed delivery is recorded and is not retried, and the subscription stays connected for the next event. If you cannot afford to miss an update, poll the page’s feed or JSON status as a backstop.
- Duplicates: rare, but possible if processing is interrupted after a
delivery. Use
event_idto discard repeats. - Address checks: the URL must resolve to a public address on every delivery. A URL that later resolves to a private address is refused.
Rotate the secret or disconnect
Open the subscription’smanage_url (also linked as Manage subscription
after connecting) and click Disconnect Webhook. Disconnecting stops all
deliveries and deletes the stored URL and signing secret.
There is no in-place secret rotation. To get a new secret, disconnect the
subscription, then connect the same URL again from the status page’s
subscribe form. The new subscription has a new secret and a new
manage_url. Submitting a URL that is still connected does not issue a new
secret; it only sends another subscription.connected test message.
