Learn
Alert webhooks
Get every DomainToolBelt alert as a signed JSON POST to your own HTTPS address, for your scripts, Slack or Zapier flows.
Who can use them
Webhooks come with a paid site plan or topped-up API credits. You get one webhook address per account, and it receives every alert your watched domains, keywords and monitors raise, the same alerts you see in the dashboard.
Set it up
- Open Settings, then Notifications, and paste an
https://address you control. - Copy the signing secret shown there and keep it on your server. You can make a new one at any time; the old one stops working at once.
- Press Send test. We send one signed test alert and show the answer your server gave.
- Switch the webhook on.
You can do the same from code with GET and PUT /v1/webhook and POST /v1/webhook/test, or ask your assistant to use the MCP tools get_webhook, set_webhook and test_webhook.
What we send
One POST per alert with a JSON body:
{
"id": "b4f1c2d0-5e2a-4c1b-9a77-0c2f1e9d3a10",
"type": "dropped",
"domain": "brewly.com",
"title": "brewly.com dropped",
"detail": "The name was deleted and can be registered again.",
"time": "2026-10-31T14:02:11.000Z",
"link": "https://domaintoolbelt.com/domain/brewly.com"
}| Header | Meaning |
|---|---|
| X-DTB-Event | alert, or test for a test send. |
| X-DTB-Delivery | The alert id. The same id on a retry, so you can skip duplicates. |
| X-DTB-Timestamp | Unix time in seconds when the POST was signed. |
| X-DTB-Signature | sha256= followed by the HMAC-SHA256 of "{timestamp}.{body}" with your signing secret, in hex. |
Event types
The type field says what happened. New types may be added, so treat an unknown type as a general alert.
| Group | Types |
|---|---|
| Ownership | registered, re_registered, renewed, registrar_change, ns_change |
| Expiry | grace_period_active, redemption_period_active, pending_delete, dropped, hold_released, registry_reserved |
| Market | listed, delisted, price_change, marketplace_change, sold |
| Auctions | auction_started, auction_ending_soon, auction_ended, auction_sold, auction_no_sale |
Check the signature
Sign the raw body, not a re-encoded copy. Reject a timestamp older than five minutes so an old request cannot be replayed.
import crypto from 'node:crypto'
// rawBody: the request body exactly as received (a string, before JSON.parse)
function verify(rawBody, headers, secret) {
const ts = headers['x-dtb-timestamp']
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false // older than 5 min
const want = 'sha256=' + crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex')
const got = String(headers['x-dtb-signature'] || '')
return got.length === want.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want))
}Answer quickly
Answer with any 2xx status within 5 seconds. Do slow work after you answer. We do not follow redirects, so a 3xx counts as a failure.
Retries and switching off
A failed delivery is tried again 3 more times over about an hour. After 20 failed tries in a row the webhook switches off and we email you; fix your server, then switch it back on in Settings.
Address rules
- HTTPS only, with a public address. Private, local and reserved addresses are refused.
- No user name or password in the URL; 500 characters at most.
- Test sends are limited to 5 every 10 minutes.