Pricing Documentation Sign up Log in

Webhooks

Security

To ensure the security and authenticity of webhook deliveries, we sign all webhook payloads using HMAC SHA-256. This allows you to verify that webhooks are genuinely sent from our service and haven't been tampered with during transmission.

Signature Verification

Each webhook request includes a signature in the X-UserCheck-Signature header. Your webhook secret is provided when you create a webhook endpoint in our dashboard.

Important: Keep your webhook secret secure and never share it.

Verification Process

  1. Extract the signature from the X-UserCheck-Signature HTTP header
  2. Generate a new signature using the same method (HMAC SHA-256 with your secret)
  3. Compare the signatures using constant-time comparison to prevent timing attacks

Implementation Examples

PHP Example

<?php
// Get the payload and signature from the webhook request
$payload = json_decode(file_get_contents('php://input'), true);
$signature = $_SERVER['HTTP_X_USERCHECK_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret';

// Generate the expected signature
function generateSignature(array $payload, string $secret): string {
    $payloadJson = json_encode($payload);
    return hash_hmac('sha256', $payloadJson, $secret);
}

// Verify using constant-time comparison
function verifySignature(array $payload, string $signature, string $secret): bool {
    $expectedSignature = generateSignature($payload, $secret);
    return hash_equals($expectedSignature, $signature);
}

// Check if the signature is valid
if (verifySignature($payload, $signature, $secret)) {
    // Process the webhook
    $event = $payload['event'];
    $domain = $payload['data']['domain'];
    
    switch ($event) {
        case 'domain.flagged.disposable':
            // Handle disposable domain flagging
            break;
        case 'domain.flagged.relay':
            // Handle relay domain flagging
            break;
        case 'domain.flagged.spam':
            // Handle spam domain flagging
            break;
    }
    
    http_response_code(200);
    echo json_encode(['status' => 'success']);
} else {
    // Invalid signature
    http_response_code(401);
    echo json_encode(['status' => 'error', 'message' => 'Invalid signature']);
}
?>

Node.js Example

const crypto = require('crypto');
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
    const payload = req.body;
    const signature = req.headers['x-usercheck-signature'] || '';
    const secret = 'your_webhook_secret';

    function generateSignature(payload, secret) {
        const payloadString = JSON.stringify(payload);
        return crypto.createHmac('sha256', secret)
                     .update(payloadString)
                     .digest('hex');
    }

    function verifySignature(payload, signature, secret) {
        const expectedSignature = generateSignature(payload, secret);
        return crypto.timingSafeEqual(
            Buffer.from(expectedSignature, 'hex'),
            Buffer.from(signature, 'hex')
        );
    }

    try {
        if (verifySignature(payload, signature, secret)) {
            // Process the webhook
            const { event, data } = payload;
            const { domain } = data;
            
            switch (event) {
                case 'domain.flagged.disposable':
                    // Handle disposable domain flagging
                    break;
                case 'domain.flagged.relay':
                    // Handle relay domain flagging
                    break;
                case 'domain.flagged.spam':
                    // Handle spam domain flagging
                    break;
            }
            
            res.status(200).json({ status: 'success' });
        } else {
            res.status(401).json({ status: 'error', message: 'Invalid signature' });
        }
    } catch (error) {
        res.status(400).json({ status: 'error', message: 'Verification failed' });
    }
});

Python Example

import json
import hmac
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook_handler():
    payload = request.json
    signature = request.headers.get('X-UserCheck-Signature', '')
    secret = 'your_webhook_secret'

    def generate_signature(payload, secret):
        payload_json = json.dumps(payload)
        return hmac.new(
            secret.encode('utf-8'),
            payload_json.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()

    def verify_signature(payload, signature, secret):
        expected_signature = generate_signature(payload, secret)
        return hmac.compare_digest(expected_signature, signature)

    if verify_signature(payload, signature, secret):
        # Process the webhook
        event = payload['event']
        domain = payload['data']['domain']
        
        if event == 'domain.flagged.disposable':
            # Handle disposable domain flagging
            pass
        elif event == 'domain.flagged.relay':
            # Handle relay domain flagging
            pass
        elif event == 'domain.flagged.spam':
            # Handle spam domain flagging
            pass
        
        return jsonify({'status': 'success'}), 200
    else:
        return jsonify({'status': 'error', 'message': 'Invalid signature'}), 401

if __name__ == '__main__':
    app.run(debug=True)
Previous
Events & Payloads