Webhooks
Receive real-time notifications when events occur in BlockSecOps. Webhooks allow you to receive HTTP POST requests to your server when specific events happen,...
Last updated: January 14, 2026
Webhooks
Receive real-time notifications when events occur in BlockSecOps.
Overview
Webhooks allow you to receive HTTP POST requests to your server when specific events happen, such as scan completions or new vulnerability findings.
How Webhooks Work
1. Event occurs (scan completes)
↓
2. BlockSecOps sends POST request to your URL
↓
3. Your server processes the event
↓
4. Your server responds with 200 OK
Creating Webhooks
Via Dashboard
- Go to Settings → Webhooks
- Click Add Webhook
- Configure:
- URL: Your endpoint (must be HTTPS)
- Events: Select events to receive
- Secret: Set signing secret (recommended)
- Click Create
Via API
curl -X POST "https://api.blocksecops.com/api/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhook",
"events": ["scan.completed", "vulnerability.critical"],
"secret": "your-signing-secret"
}'
Available Events
Scan Events
| Event | Description |
|---|---|
scan.started |
Scan has begun |
scan.completed |
Scan finished successfully |
scan.failed |
Scan encountered an error |
Vulnerability Events
| Event | Description |
|---|---|
vulnerability.found |
New vulnerability detected |
vulnerability.critical |
Critical severity finding |
vulnerability.high |
High severity finding |
vulnerability.updated |
Vulnerability status changed |
Contract Events
| Event | Description |
|---|---|
contract.uploaded |
New contract uploaded |
contract.deleted |
Contract removed |
Organization Events
| Event | Description |
|---|---|
member.invited |
New member invited |
member.joined |
Member accepted invitation |
member.removed |
Member removed |
Webhook Payload
Request Headers
Content-Type: application/json
X-BlockSecOps-Event: scan.completed
X-BlockSecOps-Signature: sha256=abc123...
X-BlockSecOps-Delivery: del_xyz789
Payload Structure
{
"id": "evt_abc123",
"type": "scan.completed",
"created_at": "2025-01-15T10:30:00Z",
"data": {
"scan_id": "scan_xyz789",
"contract_id": "contract_123",
"status": "completed",
"summary": {
"critical": 0,
"high": 2,
"medium": 5,
"low": 3
},
"duration_seconds": 45
},
"organization_id": "org_abc123"
}
Event Payloads
scan.completed
{
"type": "scan.completed",
"data": {
"scan_id": "scan_xyz789",
"contract_id": "contract_123",
"contract_name": "Token.sol",
"status": "completed",
"preset": "standard",
"summary": {
"critical": 0,
"high": 2,
"medium": 5,
"low": 3,
"total": 10
},
"duration_seconds": 45,
"scanners_used": ["slither", "mythril", "aderyn"]
}
}
vulnerability.critical
{
"type": "vulnerability.critical",
"data": {
"vulnerability_id": "vuln_abc123",
"scan_id": "scan_xyz789",
"title": "Reentrancy Vulnerability",
"severity": "critical",
"category": "reentrancy",
"file": "contracts/Token.sol",
"line": 45,
"scanner": "slither",
"risk_score": 95
}
}
scan.failed
{
"type": "scan.failed",
"data": {
"scan_id": "scan_xyz789",
"contract_id": "contract_123",
"error": "Compilation failed",
"error_details": "ParserError: Expected pragma, import directive...",
"duration_seconds": 5
}
}
Signature Verification
Verify webhook authenticity using the signature header:
Node.js
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express middleware
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-blocksecops-signature'];
const payload = req.body.toString();
if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
// Process event...
res.status(200).send('OK');
});
Python
import hmac
import hashlib
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = os.environ['WEBHOOK_SECRET']
def verify_signature(payload, signature, secret):
expected = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-BlockSecOps-Signature')
payload = request.data
if not verify_signature(payload, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.json
if event['type'] == 'scan.completed':
handle_scan_completed(event['data'])
elif event['type'] == 'vulnerability.critical':
handle_critical_vuln(event['data'])
return 'OK', 200
Handling Webhooks
Best Practices
- Respond quickly: Return 200 within 5 seconds
- Process async: Queue events for background processing
- Handle duplicates: Events may be sent multiple times
- Verify signatures: Always validate authenticity
Example Handler
const queue = require('./queue');
app.post('/webhook', async (req, res) => {
// Verify signature first
if (!verifyWebhook(req.body, req.headers)) {
return res.status(401).send('Unauthorized');
}
const event = req.body;
// Check for duplicate
if (await isDuplicate(event.id)) {
return res.status(200).send('Already processed');
}
// Queue for async processing
await queue.add('webhook', event);
// Respond immediately
res.status(200).send('Queued');
});
// Background processor
queue.process('webhook', async (job) => {
const event = job.data;
switch (event.type) {
case 'scan.completed':
await notifySlack(event.data);
await updateDashboard(event.data);
break;
case 'vulnerability.critical':
await createJiraTicket(event.data);
await sendPagerDuty(event.data);
break;
}
});
Retry Policy
Failed webhook deliveries are retried:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 8 hours |
| 7 | 24 hours |
Webhooks are considered failed if:
- Response status is not 2xx
- Connection times out (10 seconds)
- SSL verification fails
Managing Webhooks
List Webhooks
curl -X GET "https://api.blocksecops.com/api/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY"
Update Webhook
curl -X PATCH "https://api.blocksecops.com/api/v1/webhooks/{webhook_id}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": ["scan.completed", "scan.failed"]
}'
Delete Webhook
curl -X DELETE "https://api.blocksecops.com/api/v1/webhooks/{webhook_id}" \
-H "Authorization: Bearer YOUR_API_KEY"
Test Webhook
curl -X POST "https://api.blocksecops.com/api/v1/webhooks/{webhook_id}/test" \
-H "Authorization: Bearer YOUR_API_KEY"
Delivery Logs
View webhook delivery history:
Via Dashboard
Go to Settings → Webhooks → Select webhook → Deliveries
Via API
curl -X GET "https://api.blocksecops.com/api/v1/webhooks/{webhook_id}/deliveries" \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"deliveries": [
{
"id": "del_abc123",
"event_type": "scan.completed",
"status": "success",
"response_code": 200,
"response_time_ms": 150,
"created_at": "2025-01-15T10:30:00Z"
},
{
"id": "del_xyz789",
"event_type": "scan.failed",
"status": "failed",
"response_code": 500,
"error": "Server error",
"retry_count": 2,
"created_at": "2025-01-15T10:25:00Z"
}
]
}
Troubleshooting
Webhook Not Receiving Events
- Check URL is accessible from internet
- Verify HTTPS certificate is valid
- Check firewall allows BlockSecOps IPs
- Verify events are selected for webhook
Signature Verification Failing
- Ensure secret matches exactly
- Verify using raw payload (not parsed JSON)
- Check for encoding issues
Frequent Retries
- Respond within 10 seconds
- Return 200 status for successful processing
- Handle errors gracefully
IP Allowlist
BlockSecOps webhooks originate from:
52.45.123.0/24
54.87.234.0/24
Enterprise customers receive dedicated IPs.
Next Steps
- Slack Integration - Slack notifications
- Email Notifications - Email alerts
- API Overview - Full API docs