Skip to main content

Vulnerability Management Webhooks

Webhook Configuration

To set up webhooks for your integration, visit the Unizo Console Webhooks section for step-by-step configuration guide.

Overview

Unizo's Vulnerability Management API provides webhooks to notify your application when important vulnerability events occur across your security scanning and vulnerability management platforms. These real-time notifications enable you to build automated remediation workflows, maintain security posture, track vulnerability lifecycle, and ensure compliance with security policies.

Our platform normalizes webhook events from various vulnerability management providers (Qualys, Tenable, Rapid7, CrowdStrike, etc.) into a consistent format, making it easy to handle vulnerability events across different scanning platforms.

Supported Event Types

Event TypeDescriptionTrigger Conditions
Triggered when new vulnerabilities are discovered
Triggered when vulnerability details are updated
Triggered when a vulnerability is remediated
Triggered when remediation is verified
Triggered when a vulnerability scan begins
Triggered when a vulnerability scan completes
Triggered when a scan fails
Triggered when an asset's risk score changes
Triggered when a patch becomes available
Triggered when compliance violations are detected

Webhook Security

All webhooks from Unizo include security headers to verify authenticity:

Headers

HeaderDescription
x-unizo-event-typeThe type of event that triggered the webhook
x-unizo-signatureHMAC SHA-256 signature for request validation
x-unizo-timestampUnix timestamp when the event was sent
x-unizo-delivery-idUnique identifier for this webhook delivery

Signature Verification

Verify the authenticity of incoming webhooks using HMAC SHA-256:

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

Event Details

Vulnerability Events

Vulnerability Discovered

vulnerability:discovered

Triggered when new vulnerabilities are discovered during scans or continuous monitoring of your infrastructure.
POSThttps://api.yourapp.com/webhooks/unizo/scm
Best Practice: Use a dedicated webhook endpoint that can handle multiple event types. You have two architectural options:
• Single endpoint: https://api.yourapp.com/webhooks/unizo - Route all events to one handler
• Category-based endpoints: https://api.yourapp.com/webhooks/unizo/scm - Route by category (scm, ticketing, etc.) for microservices architecture
Headers
NameTypeRequiredDescription
Content-TypestringYesAlways application/json
x-unizo-event-typestringYesEvent type: vulnerability:discovered
x-unizo-webhook-idstringYesUnique webhook configuration ID
x-unizo-delivery-idstringYesUnique delivery attempt ID
x-unizo-signaturestringYesHMAC SHA256 signature for verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
data.vulnerability.idstringYesUnique vulnerability identifier
data.vulnerability.cve_idstringNoCVE identifier if available
data.vulnerability.titlestringYesVulnerability title
data.vulnerability.descriptionstringYesDetailed description
data.vulnerability.severitystringYesSeverity level (critical, high, medium, low, info)
data.vulnerability.cvss_scorenumberNoCVSS score (0-10)
data.vulnerability.cvss_vectorstringNoCVSS vector string
data.vulnerability.discovered_atstringYesDiscovery timestamp (ISO 8601)
data.vulnerability.categorystringNoVulnerability category
data.vulnerability.exploit_availablebooleanNoWhether exploit is publicly available
data.affected_assetsarrayYesList of affected assets
data.remediation.availablebooleanYesWhether remediation is available
data.remediation.typestringNoRemediation type (patch, configuration, upgrade, workaround)
data.remediation.descriptionstringNoRemediation instructions
data.remediation.effortstringNoRemediation effort (low, medium, high)
data.scan_idstringNoAssociated scan ID
integration.idstringYesIntegration ID
integration.namestringYesIntegration name
integration.providerstringYesVMS provider name
Example Payload
{
"type": "vulnerability:discovered",
"version": "1.0.0",
"data": {
"vulnerability": {
"id": "vuln_abc123",
"cve_id": "CVE-2024-12345",
"title": "Remote Code Execution in Apache Log4j",
"description": "A critical vulnerability allowing remote code execution through crafted log messages",
"severity": "critical",
"cvss_score": 10,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"discovered_at": "2024-06-15T14:30:00Z",
"category": "Remote Code Execution",
"exploit_available": true
},
"affected_assets": [
{
"id": "asset_srv_001",
"hostname": "prod-api-01",
"ip_address": "10.0.1.50",
"os": "Ubuntu 20.04",
"criticality": "critical"
},
{
"id": "asset_srv_002",
"hostname": "prod-api-02",
"ip_address": "10.0.1.51",
"os": "Ubuntu 20.04",
"criticality": "critical"
}
],
"remediation": {
"available": true,
"type": "patch",
"description": "Update Log4j to version 2.17.0 or later",
"effort": "low"
},
"scan_id": "scan_123456"
},
"integration": {
"id": "int_qualys_789",
"name": "Company Qualys",
"provider": "qualys"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Vulnerability Remediated

vulnerability:remediated

Triggered when a vulnerability has been successfully remediated through patching, configuration changes, or other mitigation measures.
POSThttps://api.yourapp.com/webhooks/unizo/scm
Best Practice: Use a dedicated webhook endpoint that can handle multiple event types. You have two architectural options:
• Single endpoint: https://api.yourapp.com/webhooks/unizo - Route all events to one handler
• Category-based endpoints: https://api.yourapp.com/webhooks/unizo/scm - Route by category (scm, ticketing, etc.) for microservices architecture
Headers
NameTypeRequiredDescription
Content-TypestringYesAlways application/json
x-unizo-event-typestringYesEvent type: vulnerability:remediated
x-unizo-webhook-idstringYesUnique webhook configuration ID
x-unizo-delivery-idstringYesUnique delivery attempt ID
x-unizo-signaturestringYesHMAC SHA256 signature for verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
data.vulnerability_idstringYesVulnerability identifier
data.cve_idstringNoCVE identifier
data.remediation.methodstringYesRemediation method (patched, configured, removed, mitigated)
data.remediation.applied_atstringYesRemediation timestamp (ISO 8601)
data.remediation.applied_by.idstringYesUser or system ID
data.remediation.applied_by.namestringYesName
data.remediation.applied_by.typestringYesRemediation source (user, automated)
data.remediation.detailsstringNoRemediation details
data.remediation.verification_statusstringYesVerification status (pending, verified, failed)
data.affected_assetsarrayYesAssets where vulnerability was remediated
data.time_to_remediateintegerNoTime from discovery to remediation in seconds
integration.idstringYesIntegration ID
integration.namestringYesIntegration name
integration.providerstringYesVMS provider name
Example Payload
{
"type": "vulnerability:remediated",
"version": "1.0.0",
"data": {
"vulnerability_id": "vuln_abc123",
"cve_id": "CVE-2024-12345",
"remediation": {
"method": "patched",
"applied_at": "2024-06-15T16:00:00Z",
"applied_by": {
"id": "auto_patch_system",
"name": "Automated Patching System",
"type": "automated"
},
"details": "Applied security update package log4j-2.17.0",
"verification_status": "verified"
},
"affected_assets": [
{
"id": "asset_srv_001",
"hostname": "prod-api-01",
"status": "remediated"
},
{
"id": "asset_srv_002",
"hostname": "prod-api-02",
"status": "remediated"
}
],
"time_to_remediate": 5400
},
"integration": {
"id": "int_tenable_456",
"name": "Company Tenable",
"provider": "tenable"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Scan Events

Scan Completed

scan:completed

Triggered when a vulnerability scan completes, providing summary results and discovered issues.
POSThttps://api.yourapp.com/webhooks/unizo/scm
Best Practice: Use a dedicated webhook endpoint that can handle multiple event types. You have two architectural options:
• Single endpoint: https://api.yourapp.com/webhooks/unizo - Route all events to one handler
• Category-based endpoints: https://api.yourapp.com/webhooks/unizo/scm - Route by category (scm, ticketing, etc.) for microservices architecture
Headers
NameTypeRequiredDescription
Content-TypestringYesAlways application/json
x-unizo-event-typestringYesEvent type: scan:completed
x-unizo-webhook-idstringYesUnique webhook configuration ID
x-unizo-delivery-idstringYesUnique delivery attempt ID
x-unizo-signaturestringYesHMAC SHA256 signature for verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
data.scan.idstringYesScan identifier
data.scan.namestringYesScan name
data.scan.typestringYesScan type (vulnerability, compliance, discovery, web_app)
data.scan.started_atstringYesScan start time (ISO 8601)
data.scan.completed_atstringYesScan completion time (ISO 8601)
data.scan.durationintegerNoScan duration in seconds
data.scan.scanner_versionstringNoScanner version used
data.summary.assets_scannedintegerYesNumber of assets scanned
data.summary.vulnerabilities_found.criticalintegerYesCritical vulnerabilities
data.summary.vulnerabilities_found.highintegerYesHigh severity vulnerabilities
data.summary.vulnerabilities_found.mediumintegerYesMedium severity vulnerabilities
data.summary.vulnerabilities_found.lowintegerYesLow severity vulnerabilities
data.summary.vulnerabilities_found.infointegerNoInformational findings
data.summary.vulnerabilities_found.totalintegerYesTotal vulnerabilities
data.summary.new_vulnerabilitiesintegerNoNewly discovered vulnerabilities
data.summary.remediated_since_lastintegerNoVulnerabilities remediated since last scan
data.summary.compliance_scorenumberNoCompliance score percentage
data.top_vulnerabilitiesarrayNoMost critical vulnerabilities found
integration.idstringYesIntegration ID
integration.namestringYesIntegration name
integration.providerstringYesVMS provider name
Example Payload
{
"type": "scan:completed",
"version": "1.0.0",
"data": {
"scan": {
"id": "scan_789xyz",
"name": "Weekly Infrastructure Scan",
"type": "vulnerability",
"started_at": "2024-06-15T02:00:00Z",
"completed_at": "2024-06-15T04:30:00Z",
"duration": 9000,
"scanner_version": "Qualys VMDR 2.5"
},
"summary": {
"assets_scanned": 250,
"vulnerabilities_found": {
"critical": 5,
"high": 23,
"medium": 87,
"low": 156,
"info": 342,
"total": 613
},
"new_vulnerabilities": 12,
"remediated_since_last": 45,
"compliance_score": 78.5
},
"top_vulnerabilities": [
{
"id": "vuln_log4j",
"cve_id": "CVE-2024-12345",
"severity": "critical",
"affected_assets": 15
},
{
"id": "vuln_openssl",
"cve_id": "CVE-2024-23456",
"severity": "critical",
"affected_assets": 8
}
]
},
"integration": {
"id": "int_rapid7_123",
"name": "Company Rapid7",
"provider": "rapid7"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Asset Events

Asset Risk Changed

asset:risk_changed

Triggered when an asset's risk score changes significantly due to new vulnerabilities, remediation, or criticality updates.
POSThttps://api.yourapp.com/webhooks/unizo/scm
Best Practice: Use a dedicated webhook endpoint that can handle multiple event types. You have two architectural options:
• Single endpoint: https://api.yourapp.com/webhooks/unizo - Route all events to one handler
• Category-based endpoints: https://api.yourapp.com/webhooks/unizo/scm - Route by category (scm, ticketing, etc.) for microservices architecture
Headers
NameTypeRequiredDescription
Content-TypestringYesAlways application/json
x-unizo-event-typestringYesEvent type: asset:risk_changed
x-unizo-webhook-idstringYesUnique webhook configuration ID
x-unizo-delivery-idstringYesUnique delivery attempt ID
x-unizo-signaturestringYesHMAC SHA256 signature for verification
Request Body Schema
PropertyTypeRequiredDescription
typestringYesEvent type identifier
versionstringYesWebhook payload version
data.asset.idstringYesAsset ID
data.asset.hostnamestringYesHostname
data.asset.ip_addressstringNoIP address
data.asset.typestringNoAsset type
data.asset.criticalitystringYesBusiness criticality
data.risk_change.previous_scoreintegerYesPrevious risk score (0-100)
data.risk_change.current_scoreintegerYesCurrent risk score (0-100)
data.risk_change.previous_levelstringYesPrevious risk level (low, medium, high, critical)
data.risk_change.current_levelstringYesCurrent risk level (low, medium, high, critical)
data.risk_change.change_reasonstringYesPrimary reason for change
data.risk_change.changed_atstringYesChange timestamp (ISO 8601)
data.vulnerability_summary.criticalintegerYesNumber of critical vulnerabilities
data.vulnerability_summary.highintegerYesNumber of high vulnerabilities
data.vulnerability_summary.mediumintegerYesNumber of medium vulnerabilities
data.vulnerability_summary.lowintegerYesNumber of low vulnerabilities
data.vulnerability_summary.exploitableintegerNoVulnerabilities with known exploits
data.recommended_actionsarrayNoRecommended response actions
integration.idstringYesIntegration ID
integration.namestringYesIntegration name
integration.providerstringYesVMS provider name
Example Payload
{
"type": "asset:risk_changed",
"version": "1.0.0",
"data": {
"asset": {
"id": "asset_db_001",
"hostname": "prod-database-01",
"ip_address": "10.0.2.100",
"type": "Database Server",
"criticality": "critical"
},
"risk_change": {
"previous_score": 65,
"current_score": 92,
"previous_level": "medium",
"current_level": "critical",
"change_reason": "New critical vulnerability with active exploit detected",
"changed_at": "2024-06-15T15:00:00Z"
},
"vulnerability_summary": {
"critical": 3,
"high": 7,
"medium": 15,
"low": 23,
"exploitable": 4
},
"recommended_actions": [
"Immediately patch CVE-2024-12345",
"Apply database security configuration baseline",
"Enable advanced threat detection",
"Review database access controls"
]
},
"integration": {
"id": "int_crowdstrike_789",
"name": "Company CrowdStrike",
"provider": "crowdstrike"
}
}
Response
200 OKWebhook processed successfully
400 Bad RequestInvalid webhook payload
401 UnauthorizedInvalid or missing signature

Webhook Delivery & Retries

Unizo implements a robust delivery system with automatic retries to ensure your webhooks are delivered reliably:

  • Timeout: 30 seconds per delivery attempt
  • Retry Schedule: 5 attempts with exponential backoff
    • Attempt 1: Immediate
    • Attempt 2: 1 minute delay
    • Attempt 3: 5 minutes delay
    • Attempt 4: 30 minutes delay
    • Attempt 5: 2 hours delay
  • Success Criteria: HTTP status codes 200-299
  • Failure Handling: After 5 failed attempts, the webhook is marked as failed

Best Practices

1. Idempotency

Always implement idempotent webhook handlers using the x-unizo-delivery-id header:

Idempotent Webhook Handler

// Example: Handling webhooks idempotently
app.post('/webhooks/vms', async (req, res) => {
const deliveryId = req.headers['x-unizo-delivery-id'];

// Check if we've already processed this delivery
const existingDelivery = await db.webhookDeliveries.findOne({ 
  deliveryId 
});

if (existingDelivery) {
  console.log(`Duplicate delivery detected: ${deliveryId}`);
  return res.status(200).json({ 
    status: 'already_processed' 
  });
}

// Process the webhook
try {
  await processVulnerabilityEvent(req.body);
  
  // Record the delivery
  await db.webhookDeliveries.create({
    deliveryId,
    processedAt: new Date()
  });
  
  res.status(200).json({ status: 'success' });
} catch (error) {
  console.error('Webhook processing failed:', error);
  res.status(500).json({ status: 'error' });
}
});

2. Automated Remediation Workflows

Implement automated remediation based on vulnerability events:

Automated Vulnerability Remediation

// Example: Automated remediation workflow
app.post('/webhooks/vms', async (req, res) => {
const { type, data } = req.body;

// Acknowledge receipt
res.status(200).json({ status: 'received' });

switch (type) {
  case 'vulnerability:discovered':
    // Check if automated remediation is possible
    if (data.vulnerability.severity === 'critical' && 
        data.remediation.available && 
        data.remediation.effort === 'low') {
      
      // Create change request
      const changeRequest = await itsmApi.createChange({
        type: 'emergency',
        title: `Critical vulnerability: ${data.vulnerability.cve_id}`,
        description: data.vulnerability.description,
        impact: data.affected_assets.length,
        remediation: data.remediation.description
      });
      
      // Schedule automated patching
      for (const asset of data.affected_assets) {
        if (asset.criticality !== 'critical') {
          await patchingApi.scheduleUpdate({
            assetId: asset.id,
            vulnerability: data.vulnerability.id,
            changeRequest: changeRequest.id,
            window: 'next-maintenance'
          });
        } else {
          // Critical assets need approval
          await notificationApi.requestApproval({
            to: 'security-team@company.com',
            asset: asset,
            vulnerability: data.vulnerability,
            action: 'emergency-patch'
          });
        }
      }
    }
    break;
    
  case 'asset:risk_changed':
    // Trigger response based on risk level
    if (data.risk_change.current_level === 'critical') {
      // Isolate high-risk assets
      await networkApi.applySecurityPolicy({
        assetId: data.asset.id,
        policy: 'restricted-access',
        reason: `Risk score increased to ${data.risk_change.current_score}`
      });
      
      // Create incident
      await incidentApi.create({
        title: `Critical risk: ${data.asset.hostname}`,
        priority: 'P1',
        description: `Asset risk increased due to: ${data.risk_change.change_reason}`,
        assignTo: 'security-operations'
      });
    }
    break;
}
});

3. Compliance and Reporting

Track vulnerability metrics for compliance and reporting:

Vulnerability Compliance Tracking

// Example: Compliance tracking and reporting
app.post('/webhooks/vms', async (req, res) => {
const { type, data, integration } = req.body;

// Track metrics
const metrics = {
  timestamp: new Date(),
  eventType: type,
  integration: integration.name,
  vulnerabilityId: data.vulnerability?.id,
  severity: data.vulnerability?.severity
};

switch (type) {
  case 'scan:completed':
    // Update compliance dashboard
    await complianceDb.scanResults.create({
      scanId: data.scan.id,
      scanType: data.scan.type,
      timestamp: data.scan.completed_at,
      results: data.summary,
      complianceScore: data.summary.compliance_score
    });
    
    // Check SLA compliance
    const slaTarget = getSLAForScanType(data.scan.type);
    const meanTimeToScan = await calculateMTTS(data.scan.type);
    
    if (meanTimeToScan > slaTarget) {
      await alertingApi.createAlert({
        type: 'sla-breach',
        message: `Scan frequency SLA breach for ${data.scan.type}`,
        current: meanTimeToScan,
        target: slaTarget
      });
    }
    
    // Generate executive report
    if (data.scan.type === 'compliance') {
      await reportingApi.generateReport({
        type: 'executive-compliance',
        data: {
          score: data.summary.compliance_score,
          criticalFindings: data.summary.vulnerabilities_found.critical,
          trend: await calculateTrend('compliance_score', 30)
        },
        recipients: ['ciso@company.com', 'compliance@company.com']
      });
    }
    break;
    
  case 'vulnerability:remediated':
    // Track remediation metrics
    await metricsDb.remediations.create({
      vulnerabilityId: data.vulnerability_id,
      timeToRemediate: data.time_to_remediate,
      method: data.remediation.method,
      automated: data.remediation.applied_by.type === 'automated'
    });
    
    // Update MTTR metrics
    const mttr = await calculateMTTR(data.vulnerability_id);
    await dashboardApi.updateMetric('mttr', mttr);
    break;
}

res.status(200).json({ status: 'tracked' });
});

Need Help?

For webhook-related support: