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

  1. Go to SettingsWebhooks
  2. Click Add Webhook
  3. Configure:
    • URL: Your endpoint (must be HTTPS)
    • Events: Select events to receive
    • Secret: Set signing secret (recommended)
  4. 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

  1. Respond quickly: Return 200 within 5 seconds
  2. Process async: Queue events for background processing
  3. Handle duplicates: Events may be sent multiple times
  4. 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 SettingsWebhooks → 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

  1. Check URL is accessible from internet
  2. Verify HTTPS certificate is valid
  3. Check firewall allows BlockSecOps IPs
  4. Verify events are selected for webhook

Signature Verification Failing

  1. Ensure secret matches exactly
  2. Verify using raw payload (not parsed JSON)
  3. Check for encoding issues

Frequent Retries

  1. Respond within 10 seconds
  2. Return 200 status for successful processing
  3. 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