Skip to main content
WP HealthKit

Webhooks

Receive real-time notifications when audits and autofix runs complete — no polling required.

Events

Subscribe to one or more of these event types when registering a webhook endpoint.

EventDescription
audit.completedFired when an audit finishes successfully. Includes the full result summary.
audit.failedFired when an audit fails due to an error (e.g. invalid ZIP, API timeout).
autofix.completedFired when an AI autofix run finishes and a patch is available for download.
autofix.failedFired when an autofix run could not produce a valid patch.

Registration

Register a webhook endpoint using the API. Your URL must use HTTPS and return a 2xx status within 10 seconds.

curl -X POST https://wphealthkit.com/api/v1/webhooks \
  -H "Authorization: Bearer whk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/wphk",
    "events": ["audit.completed", "audit.failed"],
    "secret": "your_signing_secret"
  }'

Response:

{
  "data": {
    "id": "wh_01j9x4r2v8kz3m7p6nq5w0c1d",
    "url": "https://yourapp.com/webhooks/wphk",
    "events": ["audit.completed", "audit.failed"],
    "created_at": "2025-04-10T12:00:00.000Z"
  },
  "error": null,
  "meta": { "timestamp": "2025-04-10T12:00:00.000Z" }
}

Payload Format

Payloads are sent as JSON via HTTP POST. Each payload includes the event type, a unique delivery ID, and the event-specific data.

audit.completed

{
  "id": "evt_01j9x4r2v8kz3m7p6nq5w0c1d",
  "event": "audit.completed",
  "created_at": "2025-04-10T12:01:14.000Z",
  "data": {
    "audit_id": "aud_01j9x4r2v8kz3m7p6nq5w0c1d",
    "slug": "contact-form-7",
    "plugin_name": "Contact Form 7",
    "plugin_version": "5.9.8",
    "overall_risk": "LOW",
    "findings_count": 3,
    "critical_count": 0,
    "high_count": 0,
    "report_url": "https://wphealthkit.com/results/aud_01j9x4r2v8kz3m7p6nq5w0c1d"
  }
}

audit.failed

{
  "id": "evt_02k0y5s3w9la4n8q7or6x1d2e",
  "event": "audit.failed",
  "created_at": "2025-04-10T12:00:58.000Z",
  "data": {
    "audit_id": "aud_02k0y5s3w9la4n8q7or6x1d2e",
    "slug": "broken-plugin",
    "error": "Could not extract ZIP — invalid archive"
  }
}

Signature Verification

When you register a webhook with a secret, WP HealthKit signs every delivery using HMAC-SHA256. Verify the signature to ensure the request originated from us and has not been tampered with.

Always verify signatures

Skipping signature verification allows anyone who knows your endpoint URL to send forged payloads.

The signature is sent in the X-WPHealthKit-Signature header as sha256=<hex>.

Node.js (Express)

import crypto from "crypto";

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(payload, "utf8")
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express handler
app.post("/webhooks/wphk", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-wphealthkit-signature"] as string;
  const isValid = verifyWebhookSignature(req.body.toString(), sig, process.env.WPHK_WEBHOOK_SECRET!);

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(req.body.toString());
  console.log("Received event:", event.event);

  res.json({ received: true });
});

PHP (WordPress)

add_action('rest_api_init', function() {
  register_rest_route('my-plugin/v1', '/wphk-webhook', [
    'methods'  => 'POST',
    'callback' => 'handle_wphk_webhook',
    'permission_callback' => '__return_true',
  ]);
});

function handle_wphk_webhook(WP_REST_Request $request) {
  $body = $request->get_body();
  $sig  = $request->get_header('x-wphealthkit-signature');
  $secret = defined('WPHK_WEBHOOK_SECRET') ? WPHK_WEBHOOK_SECRET : '';

  $expected = 'sha256=' . hash_hmac('sha256', $body, $secret);

  if (!hash_equals($expected, $sig ?? '')) {
    return new WP_Error('invalid_signature', 'Signature mismatch', ['status' => 401]);
  }

  $event = json_decode($body, true);
  // Handle event...
  return ['received' => true];
}

Retry Policy

If your endpoint returns a non-2xx status code or does not respond within 10 seconds, WP HealthKit will retry delivery up to 3 times using exponential backoff.

AttemptDelay after failure
1 (initial)Immediate
25 minutes
330 minutes

After 3 failed attempts the delivery is marked as abandoned. Delivery IDs are unique — use the id field to deduplicate events on your side in case retries succeed.

Register your first webhook

Use the API or the dashboard to manage webhook endpoints.

Webhooks