Introduction
Most teams discover domain problems the hard way: a site that stops resolving, a certificate that expired overnight, or a registrar change nobody approved. Polling WHOIS and DNS on a cron job can catch some of that — but it burns API credits, adds detection lag equal to your interval, and forces you to build diff logic, storage and alerting yourself.
WhoisJSON Domain Monitoring flips the model. You register critical domains once, choose which layers to watch, and attach a webhook URL. When a confirmed change or threshold warning fires, WhoisJSON sends an HTTPPOST with a typed JSON payload. Your application decides severity, opens a ticket, or pages on-call.
This guide is the integration playbook: event types, payload shapes, Node.js and Python receivers, and how to route alerts without recreating the monitoring service. It complements the security-team baseline (what to watch) and the expiry detection guide (lifecycle stages) without repeating endpoint tutorials.
Why Webhooks Beat Polling and Inbox-Only Alerts
- Lower quota burn. Continuous checks run on WhoisJSON infrastructure. You pay for monitoring slots — not for polling every domain every hour from your workers.
- Faster mean time to detect. Changes are compared against a stored baseline with confirmation logic. You receive an event when the diff is real, not when your next cron happens to run.
- Actionable automation. A JSON
POSTplugs into Slack Incoming Webhooks, Microsoft Teams, PagerDuty Events API, custom SOAR playbooks, or a thin Express/Flask handler that creates Jira issues. - Separation of concerns. Product engineers keep domain posture out of the critical request path. Security and ops own the receiver and severity rules.
Polling vs Built-In Monitoring
| Dimension | DIY polling | WhoisJSON monitoring + webhook |
|---|---|---|
| Detection lag | Equal to your cron interval | Continuous checks with change confirmation |
| Diff / baseline storage | You build it | Included |
| Credit usage | Scales with domains × frequency | Slot-based monitoring |
| Alert delivery | Email script you maintain | Email and/or webhook POST |
| Best for | Ad-hoc scripts, one-off audits | Production portfolios and SIEM ingestion |
Event Types You Can Subscribe To
Each monitor can enable one or more alert categories. Webhook deliveries use a code type | field so a single endpoint can branch safely.
| type | Fires when | Typical action |
|---|---|---|
whois_change | Confirmed WHOIS/RDAP drift (registrar, status, nameservers, dates, contacts when present) | Security review — possible hijack or authorized transfer |
dns_change | Confirmed DNS record drift vs previous snapshot | Ops review — cutover vs unauthorized NS/A/MX change |
expiration_warning | Days until domain expiry ≤ your threshold (default often 30) | Renewal ticket; escalate under 7 days |
ssl_expiration_warning | Days until TLSvalid_to ≤ SSL threshold | Cert renew runbook; page if < 7 days on production |
How to Configure a Monitoring Webhook
- Create or sign in to your WhoisJSON account and open Domain Monitors.
- Add the domain (apex or critical hostname your team owns).
- Enable the alert layers you care about: WHOIS changes, DNS changes, domain expiry, SSL expiry.
- Set thresholds for expiry warnings (domain days / SSL days).
- Enable the webhook channel and paste an HTTPS URL that accepts
POSTJSON (for examplehttps://ops.example.com/hooks/whoisjson). - Optionally keep email as a backup channel for humans who are not on-call.
Webhook Payload Reference
Every delivery is a JSON body. The envelope always includes code type | and code timestamp | , plus event-specific fields.
whois_change
{
"type": "whois_change",
"timestamp": "2026-10-05T08:14:22.101Z",
"domain": "example.com",
"changes": {
"nameserver": {
"previous": ["ns1.old-dns.net", "ns2.old-dns.net"],
"current": ["ns1.new-provider.com", "ns2.new-provider.com"]
},
"registrar": {
"previous": { "name": "Old Registrar Inc" },
"current": { "name": "New Registrar LLC" }
}
},
"currentWhois": { "...": "full WHOIS JSON snapshot" }
}
dns_change
{
"type": "dns_change",
"timestamp": "2026-10-05T08:16:03.550Z",
"domain": "example.com",
"previousDns": { "A": ["203.0.113.10"], "NS": ["ns1.old-dns.net"] },
"currentDns": { "A": ["198.51.100.20"], "NS": ["ns1.new-provider.com"] }
}
expiration_warning
{
"type": "expiration_warning",
"timestamp": "2026-10-05T09:00:01.000Z",
"domain": "example.com",
"daysRemaining": 28,
"expirationDate": "2026-11-02T00:00:00.000Z"
}
ssl_expiration_warning
{
"type": "ssl_expiration_warning",
"timestamp": "2026-10-05T09:05:44.220Z",
"domain": "example.com",
"daysRemaining": 12,
"expirationDate": "2026-10-17T12:00:00.000Z",
"certificateInfo": {
"issuer": { "CN": "Example CA", "O": "Example CA Inc" },
"valid_from": "2025-10-17T12:00:00.000Z",
"valid_to": "2026-10-17T12:00:00.000Z"
}
}Expiry warnings are rate-limited to roughly one alert per domain per day while the threshold remains crossed — so renewals do not spam your webhook every check cycle. For lifecycle detail, see how to detect domain expiry.
Node.js: Express Receiver
Acknowledge quickly with HTTP 200, then process asynchronously if work is heavy. Validate code type | before acting.
const express = require('express');
const app = express();
app.use(express.json({ limit: '1mb' }));
const SEVERITY = {
whois_change: 'high',
dns_change: 'high',
expiration_warning: 'medium',
ssl_expiration_warning: 'medium',
};
app.post('/hooks/whoisjson', async (req, res) => {
const event = req.body || {};
if (!event.type || !event.domain) {
return res.status(400).json({ error: 'invalid payload' });
}
// ACK first — then enrich / notify
res.status(200).json({ ok: true });
const severity = SEVERITY[event.type] || 'info';
if (event.type === 'expiration_warning' && event.daysRemaining <= 7) {
// escalate
}
if (event.type === 'ssl_expiration_warning' && event.daysRemaining <= 7) {
// escalate
}
await routeAlert({
severity,
domain: event.domain,
type: event.type,
event,
});
});
async function routeAlert({ severity, domain, type, event }) {
// Slack, PagerDuty, Jira, SIEM HTTP collector...
console.log(`[${severity}] ${type} on ${domain}`, JSON.stringify(event).slice(0, 200));
}
app.listen(8080);
Python: FastAPI Receiver
from fastapi import FastAPI, BackgroundTasks, HTTPException
from pydantic import BaseModel, Field
from typing import Any, Optional
app = FastAPI()
class MonitorEvent(BaseModel):
type: str
domain: str
timestamp: Optional[str] = None
daysRemaining: Optional[int] = None
changes: Optional[dict[str, Any]] = None
currentWhois: Optional[dict[str, Any]] = None
previousDns: Optional[dict[str, Any]] = None
currentDns: Optional[dict[str, Any]] = None
certificateInfo: Optional[dict[str, Any]] = None
expirationDate: Optional[Any] = None
def handle_event(event: MonitorEvent) -> None:
severity = "high" if event.type in {"whois_change", "dns_change"} else "medium"
if event.daysRemaining is not None and event.daysRemaining <= 7:
severity = "critical"
# POST to Slack / SIEM here
print(severity, event.type, event.domain)
@app.post("/hooks/whoisjson")
async def whoisjson_hook(event: MonitorEvent, background: BackgroundTasks):
if event.type not in {
"whois_change",
"dns_change",
"expiration_warning",
"ssl_expiration_warning",
}:
raise HTTPException(status_code=400, detail="unknown type")
background.add_task(handle_event, event)
return {"ok": True}
Severity Routing Patterns
One webhook URL does not mean one channel. Branch inside the receiver:
- whois_change / dns_change on production apex → PagerDuty or high-priority Slack channel (possible hijack or bad cutover). Cross-check with the hijacking signals guide.
- expiration_warning with daysRemaining > 14 → create a renewal ticket; no page.
- expiration_warning or SSL warning with daysRemaining ≤ 7 → page on-call for customer-facing hostnames.
- Staging / sandbox domains → low-priority channel or suppress unless WHOIS registrar changes.
Limits and Best Practices
- Expose only HTTPS endpoints. Reject non-TLS receivers in production.
- Return 2xx quickly. Do heavy SIEM enrichment in a queue or background task so WhoisJSON does not wait on your downstreams.
- Treat unknown
typevalues as non-fatal — log and ignore so future event types do not break your handler. - Deduplicate on
domain + type + calendar dayfor expiry events if you fan out to multiple systems. - Keep email enabled as a fail-safe while you validate the webhook path.
- Pair continuous monitoring with deploy-time checks — see CI/CD domain health checks — so misconfigurations fail before traffic shifts.
- For brand lookalikes (domains you do not own yet), use brand protection monitoring; webhooks here target domains you already control.
FAQ
What is a domain monitoring webhook?
An HTTPS endpoint you own that receives a JSON POST when WhoisJSON detects WHOIS drift, DNS drift, domain expiry risk or SSL expiry risk on a monitored domain.
Which event types does WhoisJSON send?
whois_change, dns_change, expiration_warning and ssl_expiration_warning — controlled per monitor in the dashboard.
Do I still need to poll the WHOIS API?
For continuous change detection on owned domains, no. Keep API lookups for investigations, CI gates, enrichment and one-off audits.
Are webhooks available on the free plan?
Email alerts are available on all plans. Webhook POST delivery is available on paid plans. See domain monitoring for current channel details.
Can I send alerts to Slack or a SIEM?
Yes. Point the webhook at a thin adapter that maps the payload to Slack Incoming Webhooks, PagerDuty, Elastic, Splunk HEC or your SOAR collector.
How is this different from the security monitoring baseline article?
The security baseline explains what signals matter. This article is the engineering integration: payloads, receivers and routing.
Put Domain Alerts in Your Incident Path
Add monitors for production domains, attach a webhook, and route WHOIS, DNS, expiry and SSL events to Slack or your SIEM — without building a poller.
Get API KeyDomain Monitoring