Webhooks and chat alerts
Get a message when Microsoft posts a new incident, changes its text or status, or resolves it: in a Microsoft Teams, Slack or Discord channel, or as a signed webhook to your own system. For all of Microsoft, one product family, one service or one Azure region. Free, no account.
Subscribe
How it works
- You subscribe a URL to a scope. Nothing is sent until you confirm.
- Within a minute a verification message arrives. In a chat channel it has a link: open it and press Confirm. A plain webhook gets a signed
subscription.verificationevent: POST{"token": data.token}tohttps://microsoftradar.com/api/v1/subscriptions/{id}/confirm, or opendata.confirm_url. - From then on, every event in your scope is delivered, signed. Every message has an unsubscribe link.
Events
incident.created,incident.updated(a new version: status, text, services),incident.resolved, andincident.unlisted(Microsoft’s page stopped listing it without saying it was resolved).status.changed: the status of a service or of a service in a region changed without an incident, as Microsoft’s pages report it (for example a cell in the Azure status matrix). Changes in one run come as one message.
All are sent by default; pass "events": [...] to choose (any of incident.created, incident.updated, incident.resolved, incident.unlisted, status.changed).
API
curl -s https://microsoftradar.com/api/v1/subscriptions -H 'Content-Type: application/json' \
-d '{"url": "https://example.org/hooks/microsoftradar", "service": "exchange-online"}'The answer has the subscription id, the signing secret and a manage_token. Both are shown only once. Instead of service you can send family, region, or scope: {"type": "all"}, and type (webhook, slack, teams, discord) when we cannot tell from the URL.
GET /api/v1/subscriptions/{id}withAuthorization: Bearer <manage_token>: status, the last delivery attempts and errors.POST …/{id}/test: send a test message.POST …/{id}/verify: send a new verification message (also how a switched-off subscription is switched on again).DELETE /api/v1/subscriptions/{id}: unsubscribe.
Send Content-Type: application/json with every POST and DELETE, also when there is no body: requests without it are refused as cross-site form posts.
curl -s -X DELETE https://microsoftradar.com/api/v1/subscriptions/sub_… -H 'Authorization: Bearer mgt_…' -H 'Content-Type: application/json'Payload
{
"id": "evt_3f1c…",
"type": "incident.updated",
"created_at": "2026-11-13T12:59:17.000Z",
"subscription": {
"id": "sub_…",
"scope": {
"type": "service",
"id": "exchange-online"
}
},
"data": {
"incident": {
"id": "m365-eb84974213",
"title": "Users may be unable to connect to multiple Microsoft 365 services",
"status": "investigating",
"impact": "minor",
"official_status": "Service degradation",
"official_ids": [],
"services": [
{
"id": "exchange-online",
"name": "Exchange Online"
}
],
"url": "https://microsoftradar.com/incidents/…/"
},
"version": {
"number": 2,
"captured_at": "2026-11-13T12:59:17.000Z",
"changed_words": {
"changes": [
{
"removed": "…",
"added": "…"
}
],
"added_words": 92,
"removed_words": 76
}
},
"summary": "Text edited: 92 words added, 76 removed. …",
"quote": "Scope of impact: This issue may affect users attempting to access the affected Microsoft 365 services."
},
"links": {
"unsubscribe": "https://microsoftradar.com/developers/webhooks/unsubscribe/?s=…&t=…"
}
}Chat channels get the same content as a formatted message (Slack blocks, a Teams Adaptive Card, a Discord embed), with no mentions. Like everything on MicrosoftRadar, messages quote Microsoft briefly (at most 25 words) and link to the source.
Signatures
Every delivery follows Standard Webhooks: headers webhook-id (the same on retries; use it to drop duplicates), webhook-timestamp and webhook-signature, an HMAC-SHA256 with your secret. The Standard Webhooks libraries verify it:
import { Webhook } from 'standardwebhooks';
// secret: the "whsec_…" value you got when you subscribed
const wh = new Webhook(secret.replace('whsec_', ''));
const event = wh.verify(rawBody, {
'webhook-id': req.headers['webhook-id'],
'webhook-timestamp': req.headers['webhook-timestamp'],
'webhook-signature': req.headers['webhook-signature'],
}); // throws when the signature or the time is wrongOr by hand:
const key = Buffer.from(secret.slice('whsec_'.length), 'base64');
const signed = `${id}.${timestamp}.${rawBody}`;
const expected = 'v1,' + crypto.createHmac('sha256', key).update(signed).digest('base64');
// compare with each space-separated entry of webhook-signature, and reject timestamps older than 5 minutesDelivery, retries and switching off
- Answer with any 2xx within 10 seconds. We do not follow redirects.
- Otherwise we try again after 1, 5 and 15 minutes, then 1, 3, 6 and 12 hours: 8 attempts in about 22 hours. A
429waits for yourRetry-After. 404or410ends a delivery at once. Three in a row, or a day of failures, switch the subscription off. Ask for a new verification to switch it on again.- Messages for one subscription are sent in order.
Privacy
We store the URL, the scope and the secrets that protect them. No email address, no name, no IP address. Chat webhook URLs are shown masked. HTTPS only, public host names only. Email alerts are not offered.
Last updated