Vulnerability Management Webhooks
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 Type | Description | Trigger 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
Header | Description |
---|---|
x-unizo-event-type | The type of event that triggered the webhook |
x-unizo-signature | HMAC SHA-256 signature for request validation |
x-unizo-timestamp | Unix timestamp when the event was sent |
x-unizo-delivery-id | Unique 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
• 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 architectureHeaders
Name | Type | Required | Description |
---|---|---|---|
Content-Type | string | Yes | Always application/json |
x-unizo-event-type | string | Yes | Event type: vulnerability:discovered |
x-unizo-webhook-id | string | Yes | Unique webhook configuration ID |
x-unizo-delivery-id | string | Yes | Unique delivery attempt ID |
x-unizo-signature | string | Yes | HMAC SHA256 signature for verification |
Request Body Schema
Property | Type | Required | Description |
---|---|---|---|
type | string | Yes | Event type identifier |
version | string | Yes | Webhook payload version |
data.vulnerability.id | string | Yes | Unique vulnerability identifier |
data.vulnerability.cve_id | string | No | CVE identifier if available |
data.vulnerability.title | string | Yes | Vulnerability title |
data.vulnerability.description | string | Yes | Detailed description |
data.vulnerability.severity | string | Yes | Severity level (critical, high, medium, low, info) |
data.vulnerability.cvss_score | number | No | CVSS score (0-10) |
data.vulnerability.cvss_vector | string | No | CVSS vector string |
data.vulnerability.discovered_at | string | Yes | Discovery timestamp (ISO 8601) |
data.vulnerability.category | string | No | Vulnerability category |
data.vulnerability.exploit_available | boolean | No | Whether exploit is publicly available |
data.affected_assets | array | Yes | List of affected assets |
data.remediation.available | boolean | Yes | Whether remediation is available |
data.remediation.type | string | No | Remediation type (patch, configuration, upgrade, workaround) |
data.remediation.description | string | No | Remediation instructions |
data.remediation.effort | string | No | Remediation effort (low, medium, high) |
data.scan_id | string | No | Associated scan ID |
integration.id | string | Yes | Integration ID |
integration.name | string | Yes | Integration name |
integration.provider | string | Yes | VMS 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 OK | Webhook processed successfully |
400 Bad Request | Invalid webhook payload |
401 Unauthorized | Invalid or missing signature |
Vulnerability Remediated
vulnerability:remediated
• 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 architectureHeaders
Name | Type | Required | Description |
---|---|---|---|
Content-Type | string | Yes | Always application/json |
x-unizo-event-type | string | Yes | Event type: vulnerability:remediated |
x-unizo-webhook-id | string | Yes | Unique webhook configuration ID |
x-unizo-delivery-id | string | Yes | Unique delivery attempt ID |
x-unizo-signature | string | Yes | HMAC SHA256 signature for verification |
Request Body Schema
Property | Type | Required | Description |
---|---|---|---|
type | string | Yes | Event type identifier |
version | string | Yes | Webhook payload version |
data.vulnerability_id | string | Yes | Vulnerability identifier |
data.cve_id | string | No | CVE identifier |
data.remediation.method | string | Yes | Remediation method (patched, configured, removed, mitigated) |
data.remediation.applied_at | string | Yes | Remediation timestamp (ISO 8601) |
data.remediation.applied_by.id | string | Yes | User or system ID |
data.remediation.applied_by.name | string | Yes | Name |
data.remediation.applied_by.type | string | Yes | Remediation source (user, automated) |
data.remediation.details | string | No | Remediation details |
data.remediation.verification_status | string | Yes | Verification status (pending, verified, failed) |
data.affected_assets | array | Yes | Assets where vulnerability was remediated |
data.time_to_remediate | integer | No | Time from discovery to remediation in seconds |
integration.id | string | Yes | Integration ID |
integration.name | string | Yes | Integration name |
integration.provider | string | Yes | VMS 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 OK | Webhook processed successfully |
400 Bad Request | Invalid webhook payload |
401 Unauthorized | Invalid or missing signature |
Scan Events
Scan Completed
scan:completed
• 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 architectureHeaders
Name | Type | Required | Description |
---|---|---|---|
Content-Type | string | Yes | Always application/json |
x-unizo-event-type | string | Yes | Event type: scan:completed |
x-unizo-webhook-id | string | Yes | Unique webhook configuration ID |
x-unizo-delivery-id | string | Yes | Unique delivery attempt ID |
x-unizo-signature | string | Yes | HMAC SHA256 signature for verification |
Request Body Schema
Property | Type | Required | Description |
---|---|---|---|
type | string | Yes | Event type identifier |
version | string | Yes | Webhook payload version |
data.scan.id | string | Yes | Scan identifier |
data.scan.name | string | Yes | Scan name |
data.scan.type | string | Yes | Scan type (vulnerability, compliance, discovery, web_app) |
data.scan.started_at | string | Yes | Scan start time (ISO 8601) |
data.scan.completed_at | string | Yes | Scan completion time (ISO 8601) |
data.scan.duration | integer | No | Scan duration in seconds |
data.scan.scanner_version | string | No | Scanner version used |
data.summary.assets_scanned | integer | Yes | Number of assets scanned |
data.summary.vulnerabilities_found.critical | integer | Yes | Critical vulnerabilities |
data.summary.vulnerabilities_found.high | integer | Yes | High severity vulnerabilities |
data.summary.vulnerabilities_found.medium | integer | Yes | Medium severity vulnerabilities |
data.summary.vulnerabilities_found.low | integer | Yes | Low severity vulnerabilities |
data.summary.vulnerabilities_found.info | integer | No | Informational findings |
data.summary.vulnerabilities_found.total | integer | Yes | Total vulnerabilities |
data.summary.new_vulnerabilities | integer | No | Newly discovered vulnerabilities |
data.summary.remediated_since_last | integer | No | Vulnerabilities remediated since last scan |
data.summary.compliance_score | number | No | Compliance score percentage |
data.top_vulnerabilities | array | No | Most critical vulnerabilities found |
integration.id | string | Yes | Integration ID |
integration.name | string | Yes | Integration name |
integration.provider | string | Yes | VMS 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 OK | Webhook processed successfully |
400 Bad Request | Invalid webhook payload |
401 Unauthorized | Invalid or missing signature |
Asset Events
Asset Risk Changed
asset:risk_changed
• 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 architectureHeaders
Name | Type | Required | Description |
---|---|---|---|
Content-Type | string | Yes | Always application/json |
x-unizo-event-type | string | Yes | Event type: asset:risk_changed |
x-unizo-webhook-id | string | Yes | Unique webhook configuration ID |
x-unizo-delivery-id | string | Yes | Unique delivery attempt ID |
x-unizo-signature | string | Yes | HMAC SHA256 signature for verification |
Request Body Schema
Property | Type | Required | Description |
---|---|---|---|
type | string | Yes | Event type identifier |
version | string | Yes | Webhook payload version |
data.asset.id | string | Yes | Asset ID |
data.asset.hostname | string | Yes | Hostname |
data.asset.ip_address | string | No | IP address |
data.asset.type | string | No | Asset type |
data.asset.criticality | string | Yes | Business criticality |
data.risk_change.previous_score | integer | Yes | Previous risk score (0-100) |
data.risk_change.current_score | integer | Yes | Current risk score (0-100) |
data.risk_change.previous_level | string | Yes | Previous risk level (low, medium, high, critical) |
data.risk_change.current_level | string | Yes | Current risk level (low, medium, high, critical) |
data.risk_change.change_reason | string | Yes | Primary reason for change |
data.risk_change.changed_at | string | Yes | Change timestamp (ISO 8601) |
data.vulnerability_summary.critical | integer | Yes | Number of critical vulnerabilities |
data.vulnerability_summary.high | integer | Yes | Number of high vulnerabilities |
data.vulnerability_summary.medium | integer | Yes | Number of medium vulnerabilities |
data.vulnerability_summary.low | integer | Yes | Number of low vulnerabilities |
data.vulnerability_summary.exploitable | integer | No | Vulnerabilities with known exploits |
data.recommended_actions | array | No | Recommended response actions |
integration.id | string | Yes | Integration ID |
integration.name | string | Yes | Integration name |
integration.provider | string | Yes | VMS 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 OK | Webhook processed successfully |
400 Bad Request | Invalid webhook payload |
401 Unauthorized | Invalid 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:
- Contact support at support@unizo.ai