Configure Webhooks for DNS Notifications

Webhooks allow DNSRadar to send HTTP requests to your endpoints when DNS changes are detected. This enables you to integrate DNS monitoring into your existing systems, trigger automated responses, or notify your team through custom channels.

Creating a Webhook

Send a POST request to /webhooks with your endpoint configuration:

curl -X POST https://api.dnsradar.dev/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://api.service.com/webhooks/dnsradar",
  "method": "POST",
  "secret": "your-webhook-secret-key",
  "headers": {
    "X-Custom-Header": "Value"
  }
}'

Required Fields

  • url: The endpoint URL to receive webhook notifications
  • method: HTTP method (POST, PUT, PATCH, DELETE)

Optional Fields

  • secret: Secret key for HMAC SHA256 signature verification (highly recommended)
  • headers: Custom HTTP headers as key-value pairs
  • groups: Array of group slugs to associate the webhook with

Webhook Security

DNSRadar provides built-in request signing to verify webhook authenticity. When you provide a secret parameter, DNSRadar signs each webhook request with HMAC SHA256.

curl -X POST https://api.dnsradar.dev/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://api.service.com/webhooks/dnsradar",
  "method": "POST",
  "secret": "your-webhook-secret-key",
  "headers": {
    "Authorization": "Bearer your-api-token"
  }
}'

How Request Signing Works

When a webhook is triggered, DNSRadar:

  1. Creates an HMAC SHA256 signature of the request body using your secret
  2. Sends the signature in the X-DNSRadar-Signature header
  3. Includes the event timestamp in the X-Webhook-Timestamp header (UTC)

Your endpoint should verify the signature to ensure the request came from DNSRadar and hasn't been tampered with.

Group Association

Webhooks are linked to groups, not individual monitors. This design allows efficient notification management:

Default Group

If you don't specify groups, the webhook is added to your default group:

curl -X POST https://api.dnsradar.dev/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://api.service.com/webhooks/dnsradar",
  "method": "POST"
}'

Specific Groups

Associate the webhook with one or more groups:

curl -X POST https://api.dnsradar.dev/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://api.service.com/webhooks/dnsradar",
  "method": "POST",
  "secret": "your-webhook-secret-key",
  "groups": [
    "production-servers",
    "critical-dns"
  ]
}'

Auto-Create Groups

If specified groups don't exist, they're created automatically:

curl -X POST https://api.dnsradar.dev/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://api.service.com/webhooks/dnsradar",
  "method": "POST",
  "groups": [
    "customer-new-client",
    "monitoring-tier-premium"
  ]
}'

This creates both groups and attaches the webhook to them.

API Response

The API returns the created webhook object:

{
  "uuid": "wh_abc123...",
  "created": "2026-01-08T10:30:00Z",
  "url": "https://api.service.com/webhooks/dnsradar",
  "method": "POST",
  "headers": {
    "Authorization": "Bearer token123",
    "X-Custom-Header": "value"
  },
  "is_active": true,
  "last_error": null,
  "last_executed": null
}

Response Fields

  • uuid: Unique webhook identifier (prefixed with wh_)
  • created: ISO 8601 timestamp of creation
  • is_active: Whether webhook is enabled
  • last_error: Most recent error message (if any)
  • last_executed: ISO 8601 timestamp of last execution

Webhook Payload

When a DNS change is detected, DNSRadar sends a POST request with event details:

{
  "uuid": "req_abc123...",
  "webhook_uuid": "wh_abc123...",
  "created": "2026-01-08T10:35:22Z",
  "event": {
    "event_uuid": "evt_xyz789",
    "monitor_uuid": "mon_abc123",
    "domain": "piedpiper.com",
    "subdomain": "www",
    "record_type": "A",
    "expected_value": ["1.2.3.4"],
    "previous_value": ["1.2.3.4"],
    "current_value": ["5.6.7.8"],
    "old_state": "VALID",
    "new_state": "MISMATCH",
    "occurred": "2026-01-08T10:35:22Z",
    "incidence_count": 1
  }
}

Payload Fields

The Webhook event payload contains information about the current request, and an object event containing the actual event.

  • event_uuid: Unique identifier for this event
  • monitor_uuid: UUID of the monitor that detected the change
  • domain/subdomain: DNS record location
  • record_type: Type of DNS record (A, AAAA, CNAME, MX, TXT, NS, PTR)
  • expected_value: What the monitor expects to see
  • previous_value: DNS value before the change
  • current_value: New DNS value detected
  • old_state: Previous monitor state
  • new_state: Current monitor state
  • occurred: ISO 8601 timestamp of the event
  • incidence_count: Total number of incidents for this monitor

Multiple Webhooks Per Group

Groups can have multiple webhooks, enabling:

  • Notifications to multiple channels (Slack + email)
  • Different alert severities to different endpoints
  • Redundant notification systems
  • Multi-team notifications
curl -X POST https://api.dnsradar.dev/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://hooks.slack.com/services/TEAM/WEBHOOK",
  "method": "POST",
  "groups": [
    "production-dns"
  ]
}'

Then add another webhook to the same group:

curl -X POST https://api.dnsradar.dev/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://api.pagerduty.com/incidents",
  "method": "POST",
  "headers": {
    "Authorization": "Token token=YOUR_KEY"
  },
  "groups": [
    "production-dns"
  ]
}'

Now both webhooks receive notifications for monitors in the production-dns group.

Webhook Limits

Best Practices

  1. Use HTTPS: Always use HTTPS endpoints for security
  2. Implement Authentication: Validate webhook requests using headers
  3. Handle Retries: Design endpoints to be idempotent
  4. Return Quickly: Respond to webhooks quickly (< 5 seconds)
  5. Test Webhooks: Use the test endpoint before production
  6. Monitor Webhook Health: Track webhook failures and errors

Security Considerations

Verify Webhook Signature

Validate the HMAC SHA256 signature to ensure requests come from DNSRadar:

// Node.js example with Express
const crypto = require('crypto')

app.post('/webhooks/dnsradar', (req, res) => {
  const signature = req.headers['x-dnsradar-signature']
  const timestamp = req.headers['x-webhook-timestamp']
  const secret = process.env.DNSRADAR_WEBHOOK_SECRET

  // Create HMAC signature from request body
  const body = JSON.stringify(req.body)
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex')

  // Compare signatures
  if (signature !== expectedSignature) {
    return res.status(401).send('Invalid signature')
  }

  // Process webhook
  const event = req.body
  handleDNSEvent(event)

  res.status(200).send('OK')
})
# Python example with Flask
import hmac
import hashlib
from flask import Flask, request

@app.route('/webhooks/dnsradar', methods=['POST'])
def webhook():
    signature = request.headers.get('X-DNSRadar-Signature')
    timestamp = request.headers.get('X-Webhook-Timestamp')
    secret = os.environ['DNSRADAR_WEBHOOK_SECRET']

    # Create HMAC signature from request body
    body = request.get_data()
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        body,
        hashlib.sha256
    ).hexdigest()

    # Compare signatures
    if not hmac.compare_digest(signature, expected_signature):
        return 'Invalid signature', 401

    # Process webhook
    event = request.get_json()
    handle_dns_event(event)

    return 'OK', 200
// PHP example
<?php
$signature = $_SERVER['HTTP_X_DNSRADAR_SIGNATURE'];
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'];
$secret = getenv('DNSRADAR_WEBHOOK_SECRET');

// Create HMAC signature from request body
$body = file_get_contents('php://input');
$expectedSignature = hash_hmac('sha256', $body, $secret);

// Compare signatures
if (!hash_equals($signature, $expectedSignature)) {
    http_response_code(401);
    exit('Invalid signature');
}

// Process webhook
$event = json_decode($body, true);
handleDNSEvent($event);

http_response_code(200);
echo 'OK';

Prevent Replay Attacks

Use the X-Webhook-Timestamp header to reject old requests:

const MAX_AGE = 5 * 60 * 1000 // 5 minutes

app.post('/webhooks/dnsradar', (req, res) => {
  const signature = req.headers['x-dnsradar-signature']
  const timestamp = req.headers['x-webhook-timestamp']
  const secret = process.env.DNSRADAR_WEBHOOK_SECRET

  // Verify timestamp is recent
  const eventTime = new Date(timestamp)
  const now = new Date()

  if (now - eventTime > MAX_AGE) {
    return res.status(400).send('Request too old')
  }

  // Verify signature
  const body = JSON.stringify(req.body)
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex')

  if (signature !== expectedSignature) {
    return res.status(401).send('Invalid signature')
  }

  // Process webhook
  const event = req.body
  handleDNSEvent(event)

  res.status(200).send('OK')
})
# Python replay attack prevention
from datetime import datetime, timedelta

@app.route('/webhooks/dnsradar', methods=['POST'])
def webhook():
    signature = request.headers.get('X-DNSRadar-Signature')
    timestamp = request.headers.get('X-Webhook-Timestamp')
    secret = os.environ['DNSRADAR_WEBHOOK_SECRET']

    # Verify timestamp is recent (within 5 minutes)
    event_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
    now = datetime.now(timezone.utc)

    if (now - event_time).total_seconds() > 300:
        return 'Request too old', 400

    # Verify signature
    body = request.get_data()
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        body,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected_signature):
        return 'Invalid signature', 401

    # Process webhook
    event = request.get_json()
    handle_dns_event(event)

    return 'OK', 200

IP Allowlisting

Consider restricting webhook requests to DNSRadar's IP ranges (contact support for current IP list).

Error Handling

Your webhook endpoint should always return a 2xx status code to be considered successful.

All other status code will trigger a retry.

Next Steps