# Watch domains and get every change as a webhook

Published 9 November 2026 on the [DigStory blog](https://digstory.io/blog.md).

> Watch domains, receive their DNS, WHOIS, availability and technology changes as signed webhook deliveries, and verify the signature in a few lines.

Looking up a domain tells you its past. Watching it tells you its future: every change from now on becomes an **event**, in DigStory and, if you want, delivered to your own systems.

## Start watching

Open **Watching**, type a domain and press **Watch**. You can also hit **Watch** on any domain page. Watched domains belong to your team, so everyone on it sees the same list and the same events. The free plan includes 25 watched domains, Pro 1,000 and Business 10,000.

## What counts as a change

Every event has a `kind`:

| Kind | Example | Data |
| --- | --- | --- |
| `dns` | An MX record was added | `type`, `value`, `change` (`added` or `removed`) |
| `whois` | The expiry date moved | `field`, `old`, `new` |
| `status` | The domain stopped resolving | `event` (`gone` or `back`) |
| `tech` | A CDN was adopted | `technology`, `change` |

The **Events** panel lists them newest first, and the API serves the same list at `GET /api/v1/watching/events`.

## Send them to a webhook

Team owners and admins can set a **webhook URL** and a **signing secret** in the Webhook panel. DigStory then POSTs new events to that URL as JSON, in batches:

```json
{
  "team": {"id": 7, "name": "Acme security"},
  "events": [
    {
      "id": 98121,
      "kind": "dns",
      "domain": "example.com",
      "domain_id": 4512,
      "at": "2026-11-02T08:14:00Z",
      "data": {"family": "name", "type": "mx", "value": "mail.example.net", "change": "added"}
    }
  ]
}
```

Events that fail to deliver are kept and retried, and you can trigger a delivery by hand from the panel at any time.

## Verify the signature

When a secret is set, every delivery carries an `x-digstory-signature` header: `sha256=` followed by the hex HMAC-SHA256 of the raw request body under your secret. Compute the same value and compare before you trust the payload.

**Node.js**

```javascript
import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return header.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}
```

**Python**

```python
import hashlib, hmac

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(header, expected)
```

Always sign over the **raw** body, before any JSON parsing, and use a constant-time comparison.

## Ideas for what to do with them

- Post `dns` and `status` events for your own domains to a team chat channel, so an unexpected change gets noticed in minutes, not days.
- Open a ticket when a `whois` event shows a customer's or partner's domain nearing expiry.
- Feed `tech` events for competitors' domains into your CRM to see who just switched providers.
- Watch lookalike domains of your brand and alert when one starts resolving.
