Agent skill

Bitdefender Webhooks Skill

Receive and authenticate Bitdefender GravityZone Event Push webhooks. Use when setting up a Bitdefender jsonRPC push endpoint with setPushEventSettings, checking the Authorization header credential, debugging 401s, or handling addEvents notifications such as av, aph, avc, fw, hd and dp events.

Install this skill

npx skills add hookdeck/webhook-skills --skill bitdefender-webhooks


Bitdefender GravityZone (endpoint security) pushes security events to your server through the Event Push Service. You turn it on with the setPushEventSettings API using serviceType: "jsonRPC". Payloads are not signed. The credential is the Authorization header you configure in serviceSettings.authorization.

When to Use This Skill

  • How do I receive Bitdefender GravityZone webhooks (Event Push Service)?
  • How do I verify the Authorization header on Bitdefender push events?
  • Does Bitdefender sign webhook payloads? (No: shared Authorization credential only)
  • Why does my Bitdefender webhook endpoint return 401?
  • How do I handle addEvents JSON-RPC messages and the module values (av, aph, fw)?

Verification (core)

serviceSettings.authorization is sent verbatim as the Authorization header (the docs' example is Bearer sfasdfw34243). Paste the authorization value exactly as set in GravityZone, including any "Bearer " or "Basic " prefix, into BITDEFENDER_AUTHORIZATION, and compare the whole header to it. Do not strip, parse or base64-decode the prefix:

const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyBitdefenderRequest(headers, expected = process.env.BITDEFENDER_AUTHORIZATION) {
  if (!expected) return false;                 // never accept when unconfigured
  const received = headers['authorization'];   // full value, e.g. "Bearer sfasdfw34243"
  return Boolean(received) && safeEqual(received, expected);
}

Return 401 when the header is missing or does not match, 400 for a body that is not an addEvents call, and 200 once handled. Read the body raw (express.raw({ type: '*/*' })) so any content type works, and parse it only after the check. In Python use hmac.compare_digest on bytes (encode both sides; a non-ASCII str raises).

What this does and does not give you: a static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS and rotate the credential with setPushEventSettings if it leaks. See references/verification.md.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Message Shape and Event Modules

Every push is a JSON-RPC 2.0 call to addEvents with a batch of events:

{"jsonrpc": "2.0", "method": "addEvents", "params": {"events": [{"module": "av", "...": "..."}]}, "id": 1694701009244}

The event type is each event's module value (exact strings from the docs):

moduleEvent
avAntimalware detection
aphAntiphishing
avcAdvanced Threat Control (ATC)
fwFirewall
hdHyper Detect
dpData Protection
network-sandboxingSandbox Analyzer detection

Others include modules, registration, task-status, uc, install, uninstall, antiexploit, network-monitor, ransomware-mitigation and new-incident. See references/overview.md. Respond with any 2xx; anything else counts as an error.

Environment Variables

BITDEFENDER_AUTHORIZATION=Bearer your-secret   # full header value, prefix included

Local Development

npx hookdeck-cli listen 3000 bitdefender --path /webhooks/bitdefender

No account required. Use the printed URL as serviceSettings.url. GravityZone requires an HTTPS endpoint with TLS 1.2 or higher.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 6, 2026

View on GitHub →