Notifications and webhooks
PXL Consent can tell you when a scan finds something new or monitoring finds a problem. Add channels under Organisation → Notifications. Admins manage them, and each channel chooses its own events.
Events
| Event | Sent when |
|---|---|
scan.changed | A scan found cookies, storage or third-party hosts that the previous scan did not, or no longer found some |
incident.opened | Monitoring found a problem on a site |
incident.escalated | An incident was not acknowledged within 72 hours |
incident.resolved | A later check no longer found the problem |
scan.no_consent_needed | A scan found nothing on a site that needs the visitor's consent |
scan.completed | Any scan finished |
scan.failed | A scan could not finish |
Slack
Create an incoming webhook for the channel in Slack (an app with Incoming Webhooks turned on) and paste its address, which starts with https://hooks.slack.com/. Messages are one line with a link to the dashboard.
Microsoft Teams
In the Teams channel, add the workflow Post to a channel when a webhook request is received and paste the address it gives you. Messages arrive as an adaptive card with a button to the dashboard.
Webhooks
A webhook receives a JSON POST for each event:
{
"id": "evt_4f1c…",
"event": "scan.changed",
"created_at": "2026-09-16T12:00:00Z",
"organization_id": 42,
"site": { "id": 7, "public_id": "0f4c9e2a-…", "name": "Kunde AS" },
"text": "Changes on Kunde AS. New since the last scan: _fbp (before consent).",
"link": "https://consent.pxl.as/sites/7/cookies",
"data": {
"scan_id": 311, "previous_scan_id": 298,
"added": [{ "kind": "cookie", "name": "_fbp", "domain": "kunde.no", "category": "marketing", "provider": "Meta", "before_consent": true }],
"removed": []
}
}
Incident events carry incident_id, kind, severity, summary and details in data.
Headers and signature
| Header | Value |
|---|---|
X-PXL-Event | The event name |
X-PXL-Delivery | A delivery number, the same on every retry |
X-PXL-Signature | t=<unix time>,v1=<signature> |
The signature is the hex HMAC-SHA256 of the time, a full stop and the raw request body, keyed with the channel's signing secret (shown in the channel's details). Check it before trusting the request, and reject requests whose time is more than five minutes old:
import crypto from 'node:crypto'
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false
const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex')
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
}
Delivery and retries
Any 2xx answer counts as delivered. Anything else is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours, then given up. A channel that fails 20 times in a row is turned off; turn it on again in its details once the receiver works. The details also list the latest deliveries and their errors. Use Test to send a message straight away.
Addresses must use https and point to the public internet.