Home/Docs/Webhooks

Webhook Documentation

Receive real-time HTTP notifications when scans complete, sites go down, or recover. Configure endpoints, verify signatures, and integrate with your existing tools.

HMAC-SHA256 Signedv1.0

Webhooks

WPSentry sends HTTP POST requests to your configured URLs when events occur in your account. Use webhooks to get notified in real-time when scans finish, monitored sites go down, or recover from downtime — without polling the API.

Webhooks are available on Pro (up to 3 webhooks) and Enterprise (up to 10 webhooks) plans.

Configure webhooks in your Account Settings.


Setup

Follow these steps to configure your first webhook:

  1. Go to Account > Webhooks tab.
  2. Click "Create Webhook".
  3. Enter your HTTPS endpoint URL.
  4. Select which events to subscribe to.
  5. Copy the signing secret (shown only once).

Note: Your endpoint URL must use HTTPS. The signing secret is used to verify that incoming requests are genuinely from WPSentry — store it securely.


Event Types

Subscribe to one or more of the following events:

ParameterTypeDescription
scan.completedstringFired when a security scan finishes. Includes score, issue counts, and scan ID.
site.downstringFired when a monitored site transitions from up to down.
site.upstringFired when a monitored site recovers from downtime.
webhook.teststringFired when you click the Test button. Used for verifying your endpoint.

Payload Format

All webhook payloads follow the same envelope structure:

Envelopejson
{
  "event": "scan.completed",
  "timestamp": "2025-01-15T12:00:00.000Z",
  "data": { ... }
}

scan.completed

scan.completed payloadjson
{
  "event": "scan.completed",
  "timestamp": "2025-01-15T12:00:00.000Z",
  "data": {
    "scanId": "clx1abc2d0001...",
    "url": "https://example.com",
    "domain": "example.com",
    "score": 72,
    "issueCount": 8,
    "criticalCount": 1,
    "highCount": 3,
    "pluginCount": 12,
    "vulnerablePluginCount": 2
  }
}

Data Fields

ParameterTypeDescription
scanIdstringUnique scan identifier
urlstringFull URL that was scanned
domainstringDomain name
scorenumberSecurity score (0-100)
issueCountnumberTotal number of issues found
criticalCountnumberNumber of critical issues
highCountnumberNumber of high severity issues
pluginCountnumberNumber of detected plugins
vulnerablePluginCountnumberNumber of plugins with known vulnerabilities

site.down

site.down payloadjson
{
  "event": "site.down",
  "timestamp": "2025-01-15T12:00:00.000Z",
  "data": {
    "siteId": "clx1abc2d0002...",
    "url": "https://example.com",
    "statusCode": 503,
    "errorMsg": "HTTP 503",
    "consecutiveDown": 1
  }
}

Data Fields

ParameterTypeDescription
siteIdstringMonitored site identifier
urlstringSite URL
statusCodenumber|nullHTTP status code (null if connection failed)
errorMsgstring|nullError message
consecutiveDownnumberNumber of consecutive failed checks

site.up

site.up payloadjson
{
  "event": "site.up",
  "timestamp": "2025-01-15T12:00:00.000Z",
  "data": {
    "siteId": "clx1abc2d0002...",
    "url": "https://example.com",
    "statusCode": 200,
    "responseMs": 342,
    "downDurationSeconds": 600
  }
}

Data Fields

ParameterTypeDescription
siteIdstringMonitored site identifier
urlstringSite URL
statusCodenumberHTTP status code
responseMsnumberResponse time in milliseconds
downDurationSecondsnumberHow long the site was down in seconds

Verifying Signatures

Every webhook delivery includes an X-WPSentry-Signature header containing an HMAC-SHA256 signature of the raw request body, using your webhook's signing secret.

Headers

ParameterTypeDescription
X-WPSentry-Signaturestringsha256=<hex digest> HMAC-SHA256 signature
X-WPSentry-EventstringEvent type (e.g. scan.completed)
X-WPSentry-DeliverystringUnique delivery ID
Content-TypestringAlways application/json
User-AgentstringWPSentry-Webhook/1.0

Node.js Verification

Node.jsjavascript
const crypto = require("crypto");

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return signature === `sha256=${expected}`;
}

// Express.js example
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-wpsentry-signature"];
  if (!verifyWebhook(req.body, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
  const payload = JSON.parse(req.body);
  console.log("Event:", payload.event, payload.data);
  res.status(200).send("OK");
});

Python Verification

Pythonpython
import hmac
import hashlib

def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    return signature == f"sha256={expected}"

PHP Verification

PHPphp
function verifyWebhook(string $body, string $signature, string $secret): bool {
    $expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
    return hash_equals($expected, $signature);
}

Retry Policy

If your endpoint returns a non-2xx status code or is unreachable, WPSentry retries delivery with exponential backoff:

AttemptDelay
Attempt 1Immediate
Attempt 2After 1 minute
Attempt 3After 5 minutes
Attempt 4After 30 minutes
Attempt 5After 2 hours

After 5 failed attempts, the delivery is marked as permanently failed.

If a webhook accumulates 10 consecutive delivery failures (across any deliveries), it is automatically disabled. You can re-enable it from the dashboard.

Successful deliveries reset the failure counter.


API Management

You can also manage webhooks programmatically via the REST API. All endpoints require authentication via session cookie.

GET/api/user/webhooksList all webhooks
cURLbash
curl -X GET https://yourdomain.com/api/user/webhooks \
  -H "Cookie: wp_scanner_session=YOUR_SESSION"
POST/api/user/webhooksCreate a new webhook
ParameterTypeDescription
namestringDisplay name for the webhook
urlstringHTTPS endpoint URL
eventsstring[]Array of event types to subscribe to
cURLbash
curl -X POST https://yourdomain.com/api/user/webhooks \
  -H "Content-Type: application/json" \
  -H "Cookie: wp_scanner_session=YOUR_SESSION" \
  -d '{"name":"My Webhook","url":"https://example.com/hook","events":["scan.completed"]}'
PATCH/api/user/webhooks/:idUpdate a webhook
cURLbash
curl -X PATCH https://yourdomain.com/api/user/webhooks/WEBHOOK_ID \
  -H "Content-Type: application/json" \
  -H "Cookie: wp_scanner_session=YOUR_SESSION" \
  -d '{"isActive":false}'
DELETE/api/user/webhooks/:idDelete a webhook
cURLbash
curl -X DELETE https://yourdomain.com/api/user/webhooks/WEBHOOK_ID \
  -H "Cookie: wp_scanner_session=YOUR_SESSION"
POST/api/user/webhooks/:id/testSend a test delivery
cURLbash
curl -X POST https://yourdomain.com/api/user/webhooks/WEBHOOK_ID/test \
  -H "Cookie: wp_scanner_session=YOUR_SESSION"
GET/api/user/webhooks/:id/deliveriesView delivery history
cURLbash
curl -X GET https://yourdomain.com/api/user/webhooks/WEBHOOK_ID/deliveries \
  -H "Cookie: wp_scanner_session=YOUR_SESSION"

Integration Examples

Slack

Forward WPSentry events to a Slack channel via Incoming Webhooks.

Slack Integrationjavascript
// Slack Incoming Webhook integration
// Set your Slack webhook URL as the WPSentry webhook endpoint

// Or use a proxy that formats the message:
app.post("/wpsentry-to-slack", express.json(), async (req, res) => {
  const { event, data } = req.body;

  let text;
  if (event === "scan.completed") {
    const emoji = data.score >= 80 ? ":white_check_mark:" : ":warning:";
    text = `${emoji} Scan completed for *${data.domain}*\nScore: ${data.score}/100 | Issues: ${data.issueCount}`;
  } else if (event === "site.down") {
    text = `:red_circle: *${data.url}* is DOWN\nError: ${data.errorMsg}`;
  } else if (event === "site.up") {
    text = `:large_green_circle: *${data.url}* is back UP\nDowntime: ${Math.round(data.downDurationSeconds / 60)} minutes`;
  }

  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text }),
  });

  res.status(200).send("OK");
});

Discord

Forward WPSentry events to a Discord channel via Discord Webhooks.

Discord Integrationjavascript
// Discord webhook integration
app.post("/wpsentry-to-discord", express.json(), async (req, res) => {
  const { event, data } = req.body;

  let content;
  if (event === "scan.completed") {
    content = `**Scan Complete:** ${data.domain} — Score: ${data.score}/100 (${data.issueCount} issues)`;
  } else if (event === "site.down") {
    content = `**Site Down:** ${data.url} — ${data.errorMsg}`;
  } else if (event === "site.up") {
    content = `**Site Recovered:** ${data.url} — Down for ${Math.round(data.downDurationSeconds / 60)}m`;
  }

  await fetch(process.env.DISCORD_WEBHOOK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ content }),
  });

  res.status(200).send("OK");
});