# Webhook (/en/docs/platform/features/alerting/channels/webhook)



A webhook channel POSTs each alert as JSON to an endpoint you run, signed with a shared secret so you can verify it came from us. Use it for anything the built-in chat types do not cover, such as PagerDuty or Opsgenie ingestion, a ticketing system, or your own automation.

## Prerequisites [#prerequisites]

* The website **Manager** role in CentralCSP.
* An https endpoint on a public host that accepts POST requests. Internal and private addresses are rejected, including `localhost`, `.local`, `.internal`, hosts with no dot, and anything resolving to a private IP range.

## 1. Add the channel [#1-add-the-channel]

1. In the dashboard, go to **Alerts** > **Configuration** > **Add channel**.
2. Name the channel, choose the **Webhook** type, and enter your endpoint URL.
3. Enter a **signing secret** of at least 8 characters. Generate a random one and store it where your endpoint can read it.
4. Save.

The secret is stored and never returned by the API. Editing the channel later shows a masked preview; leave the field empty to keep the current value.

## 2. Handle the request [#2-handle-the-request]

Each delivery is a POST with a JSON body and two headers:

```http
POST /your-endpoint HTTP/1.1
content-type: application/json
x-centralcsp-timestamp: 1754640000000
x-centralcsp-signature: sha256=8b1a9953c4611296a827abf8c47804d7...
```

The body carries the event, the rule, the website, and the findings:

```json
{
  "id": "0198f2c1-7d4e-7a21-b3aa-2f6f0c9d4e11",
  "eventType": "csp-violation:new-type",
  "rule": "New violation types",
  "website": {
    "id": "0198f2c1-1111-7a21-b3aa-2f6f0c9d4e22",
    "name": "Shop",
    "url": "https://shop.example.com"
  },
  "findings": [
    {
      "kind": "csp-violation:new-type",
      "key": "script-src\nhttps://cdn.evil.example",
      "effectiveDirective": "script-src",
      "blockedUrlOrigin": "https://cdn.evil.example",
      "firstSeen": "2026-08-27T09:12:00.000Z",
      "count": 41
    }
  ],
  "findingsCount": 1,
  "window": {
    "start": "2026-08-27T09:00:00.000Z",
    "end": "2026-08-27T09:15:00.000Z"
  },
  "dashboardUrl": "https://app.centralcsp.com/..."
}
```

The `findings` array shape depends on `eventType`; each finding carries a `kind` matching the event. Respond with any **2xx** status. Anything else counts as a failure: redirects are not followed, and after five consecutive failures the channel [disables itself](/en/docs/platform/features/alerting/channels#channels-turn-themselves-off).

## 3. Verify the signature [#3-verify-the-signature]

The signature is an HMAC-SHA256 of `` `${timestamp}.${body}` `` using your secret, hex encoded, prefixed with `sha256=`. Verify it, and reject stale timestamps to block replays:

```js title="verify.js"
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(secret, headers, rawBody) {
  const timestamp = headers["x-centralcsp-timestamp"];
  const received = headers["x-centralcsp-signature"];
  if (!timestamp || !received) return false;

  // Reject anything older than 5 minutes.
  if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60 * 1000) return false;

  const expected =
    "sha256=" +
    createHmac("sha256", secret)
      .update(`${timestamp}.${rawBody}`)
      .digest("hex");
  return (
    expected.length === received.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(received))
  );
}
```

Compute the HMAC over the **raw request body**, before any JSON parsing; a re-serialized body will not match.

## 4. Test the channel [#4-test-the-channel]

Select **Test** on the new channel. A sample alert built from a fake CSP violation is sent immediately, signed like any real delivery, so you can develop your verification against it.

## Next steps [#next-steps]

* [Create a rule](/en/docs/platform/features/alerting/rules) that delivers to this channel.
* [Channels overview](/en/docs/platform/features/alerting/channels) for auto-disable behaviour and limits.
* [Delivery history](/en/docs/platform/features/alerting/deliveries) to audit what was sent.
