Slack, Teams & Webhooks
Guides
Slack, Teams and webhook alerts
Send Scanverra alerts to a Slack channel, a Microsoft Teams channel or your own HTTPS endpoint, so your team hears about a score drop or a risky change where it already works.
How it works
- 1Open Team, then Integrations, and add Slack, Microsoft Teams or a webhook.
- 2Pick which alerts it should receive, then click Send test to check the message arrives.
- 3When a scheduled scan or a monitor finds something, Scanverra posts the alert as soon as the run finishes.
Scans someone runs by hand don't send alerts. Schedule the sites and repositories you want watched on the Scheduled scans page.
Alerts you can send
Each integration receives only the alerts you tick. In a webhook, the event name arrives in the X-Scanverra-Event header and the event field.
| Event | Sent when |
|---|---|
scan.score_dropped | A scheduled scan scores more than 10 points lower than its previous run. The alert lists what's new. |
scan.completed | Any scheduled scan finishes with a result. |
scan.policy_failedEnterprise | A scheduled scan fails your team policy (a minimum score, or no critical findings). The alert lists the reasons. |
payment_page.scripts_changedEnterprise | A watched payment page loads a new or changed script, or its Content-Security-Policy changes. |
attack_surface.changedEnterprise | A watched domain gains a new host, or a host becomes a takeover risk. |
test | Someone clicks Send test. |
Connect Slack
- 1In Slack, open Apps, find Incoming Webhooks and add it to the channel that should get alerts.
- 2Copy the webhook URL. It starts with https://hooks.slack.com/.
- 3In Scanverra, add an integration, choose Slack, paste the URL and pick your alerts.
Each alert shows the headline, up to five new issues and an Open report button.
Connect Microsoft Teams
- 1In the Teams channel, open Workflows and choose Post to a channel when a webhook request is received.
- 2Finish the workflow and copy the URL it gives you.
- 3In Scanverra, add an integration, choose Microsoft Teams, paste the URL and pick your alerts.
Each alert arrives as a card with the headline, the new issues and an Open report button.
Webhooks
Use a webhook to send alerts anywhere else: open a ticket, page whoever is on call, or store them. Scanverra sends one HTTPS POST with a JSON body for each alert.
Request headers
Content-Type | Always application/json. |
User-Agent | Identifies Scanverra as the sender.Scanverra-Alerts/1.0 |
X-Scanverra-Event | The event name, the same as the event field in the body.scan.score_dropped |
X-Scanverra-Timestamp | When the request was signed, in Unix seconds.1762594212 |
X-Scanverra-Signature | sha256= followed by the HMAC-SHA256 of the timestamp, a dot and the raw body, keyed with your signing secret.sha256=9f2c… |
Request body
An example of a score drop:
{
"event": "scan.score_dropped",
"sentAt": "2026-11-08T09:30:12.418Z",
"summary": "Security Scan score dropped from 81 to 62 for shop.example.com",
"data": {
"target": "https://shop.example.com",
"scanType": "security",
"score": 62,
"prevScore": 81,
"reportUrl": "https://www.scanverra.com/security-scan/cm9x2k1",
"newIssues": [
"Missing Content-Security-Policy header",
"jQuery 3.4.1 has known vulnerabilities"
]
}
}Fields
event | The event name. |
sentAt | When the alert was sent (ISO 8601). |
summary | A one-line description, ready to show as it is. |
data.target | The scanned URL, domain, or owner/repo for a repository. |
data.scanType | audit, seo, security, browser, repo or crawl (a multi-page SEO crawl). |
data.score | The new score from 0 to 100, or null when the alert has no score (monitors and tests). |
data.prevScore | The previous run's score, or null. |
data.reportUrl | A link to the report or the monitor. |
data.newIssues | Short lines describing what changed. Can be empty or missing. |
Verify the signature
When you add a webhook, Scanverra shows its signing secret (whsec_...) once. Store it on your server, then check every request before you trust it:
- 1Read the raw request body exactly as it arrived, before any JSON parsing.
- 2Compute HMAC-SHA256 of the timestamp header, a dot and the raw body, using the secret as the key.
- 3Compare it with the signature header in constant time, and reject requests whose timestamp is more than 5 minutes old.
import crypto from "node:crypto";
import express from "express";
const SECRET = process.env.SCANVERRA_WEBHOOK_SECRET; // whsec_...
const app = express();
// express.raw keeps the exact bytes Scanverra signed.
app.post("/scanverra", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.get("X-Scanverra-Timestamp") ?? "";
const signature = req.get("X-Scanverra-Signature") ?? "";
const expected = "sha256=" + crypto
.createHmac("sha256", SECRET)
.update(`${timestamp}.${req.body}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
const valid = signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!fresh || !valid) return res.sendStatus(401);
const alert = JSON.parse(req.body);
console.log(alert.event, alert.summary);
res.sendStatus(204); // answer fast, do slow work afterwards
});
app.listen(3000);Signing the timestamp with the body means a request someone captured can't be replayed later.
Delivery rules
- Answer with any 2xx status within 5 seconds. Do slow work after you respond.
- Each alert is sent once and isn't retried. The Integrations panel shows whether the last delivery worked.
- Redirects aren't followed, so use the final URL.
- Only public HTTPS addresses are accepted. Slack URLs must be on hooks.slack.com, and Teams URLs on webhook.office.com or Power Automate.
- Alerts stop for the whole team if its Team or Enterprise plan lapses.
Ready to connect?
Team owners and admins can add an integration in a minute. Send a test to check it before the first real alert.