
# 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:

```
POST https://api.dnsradar.dev/webhooks

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.

```
POST https://api.dnsradar.dev/webhooks

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.

> ⚠️ **WARNING:** **Security Best Practice**: Always provide a `secret` parameter and validate the signature on your endpoint. This prevents unauthorized requests and replay attacks.

## 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:

```
POST https://api.dnsradar.dev/webhooks

url: https://api.service.com/webhooks/dnsradar
  method: POST
```

### Specific Groups

Associate the webhook with one or more groups:

```
POST https://api.dnsradar.dev/webhooks

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:

```
POST https://api.dnsradar.dev/webhooks

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:

```json
{
  "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:

```json
{
  "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

```
POST https://api.dnsradar.dev/webhooks

url: https://hooks.slack.com/services/TEAM/WEBHOOK
  method: POST
  groups: ["production-dns"]
```

Then add another webhook to the same group:

```
POST https://api.dnsradar.dev/webhooks

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

> ℹ️ **INFO:** **Per-Group Limit**: Each group can have up to 5 webhooks (default).
This ensures efficient notification delivery while preventing excessive webhook calls.

If you need more than 5 webhooks for a group, feel free to reach out to us with your use case and we'll increase the limit accordingly.

## 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](/docs/test-webhooks) 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:

```javascript
// 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
# 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
// 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:

```javascript
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
# 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

- [Test Your Webhooks](/docs/test-webhooks)
- [Enable/Disable Webhooks](/docs/enable-disable-webhooks)
- [Understand Webhook Retries](/docs/webhook-retries)
- [View Webhook Request History](/docs/webhook-requests)
