Workspace
Notifications and webhooks
Send DNS change notifications by email, Slack, or signed webhook. Choose event types for pushes, drift, cutovers, and failures, then test each channel.
4 min read
On this page
Notifications send selected migration, cutover, zone and backup events by email, Slack or signed HTTPS webhook. The key point for webhook consumers is to verify X-DNSMigrator-Signature against the unmodified request bytes before parsing JSON, using the signing secret shown once when the channel is created.
Channel types#
Members can view configured channels. Admins and Owners can add, test, pause, resume or delete them.
| Channel | Target | Delivery format |
|---|---|---|
| One address, or every workspace Owner and Admin when Email address is empty | Subject contains the event title and workspace name; body contains the message and optional app link | |
| Slack | A Slack incoming webhook at https://hooks.slack.com/… | Slack text JSON with a bold title, message and optional Open in DNSMigrator link |
| Webhook | A public HTTPS endpoint | Signed JSON with event, workspace, time, title, body and optional URL/data |
Slack and webhook URLs are encrypted with the same envelope-encryption system used for provider credentials. The list shows only the scheme, host and an ellipsis. URLs must use HTTPS, cannot contain a username or password, and pass the outbound SSRF guard. Slack targets must use the exact hooks.slack.com hostname.
Add a channel#
Choose a channel type
Go to Notifications → Add a channel. Select Email, Slack or Webhook under Channel type.
Name and address it
Enter Name. For email, fill Email address or leave it empty to notify every Owner and Admin. For Slack, paste a Slack incoming webhook URL. For a webhook, enter the public Endpoint URL.
Pick events
Under Send me, select at least one event. The form initially selects migration failures, cutover failures, zone drift, zone push failures and backup failures.
Save the channel
Select Add channel. For a webhook, copy the value under Save your signing secret now. It is returned only in this response and is not shown again.
Test delivery
In Channels, select Send test. A successful test shows Test sent. A failure appears as Last delivery failed: followed by the endpoint or mail error.
Use Pause to leave a channel configured without enqueueing new matching events, then Resume to enable it. Delete removes the channel and its encrypted endpoint secret.
Event names#
The event name appears in X-DNSMigrator-Event and the JSON event property.
| Event | UI label | Emitted when |
|---|---|---|
migration.previewed | Preview ready | A migration preview finishes |
migration.applied | Migration applied | Destination apply finishes |
migration.verified | Migration verified | Authoritative verification finishes |
migration.failed | Migration needs attention | Preview, apply, verification or rollback fails, or verification finds mismatches |
migration.rolled_back | Migration rolled back | Rollback finishes |
cutover.step | Cutover step finished | A non-final cutover step completes |
cutover.completed | Cutover complete | The cutover workflow reaches done |
cutover.failed | Cutover needs attention | A cutover step pauses as failed |
zone.drift | Zone drift detected | A managed-zone check finds drift |
zone.pushed | Zone changes pushed | A managed-zone push succeeds |
zone.push_failed | Zone push failed | A managed-zone push finishes with one or more provider failures |
backup.failed | Backup failed | A scheduled or requested backup fails |
The current form also offers Provider access failed (connection.error), but connection checks do not enqueue that event in the current service. Selecting it alone will not produce a delivery. Use the Connections status and Activity until that emitter is wired.
Webhook request#
Webhook delivery uses POST, does not follow redirects and waits up to 10 seconds for the endpoint. Any non-success HTTP status is a failure. The request includes:
Content-Type: application/json
User-Agent: DNSMigrator-Webhooks/1
X-DNSMigrator-Event: migration.verified
X-DNSMigrator-Signature: t=UNIX_SECONDS,v1=HEX_HMACThe raw JSON has this shape:
type DNSMigratorWebhook = {
id: string;
event: string;
workspaceId: string;
createdAt: string;
title: string;
body: string;
url?: string;
data?: Record<string, unknown>;
test?: true;
};id identifies the outbox delivery. createdAt is serialized as an ISO timestamp. Event producers may include an app url and structured data; migration events currently include the migration ID and internal event kind. A test request uses the first event selected on the channel (or migration.previewed if none is present) and includes test: true.
Verify the signature#
DNSMigrator computes HMAC-SHA256 over timestamp + "." + rawBody and encodes the digest as lowercase hexadecimal. This Node.js example validates the header using a constant-time comparison and returns the signed timestamp so your application can enforce its own replay window.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyDNSMigratorWebhook(
rawBody: string,
header: string | null,
signingSecret: string,
): number {
const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? "");
if (!match) throw new Error("Malformed DNSMigrator signature");
const timestamp = Number(match[1]);
const supplied = Buffer.from(match[2], "hex");
const expected = Buffer.from(
createHmac("sha256", signingSecret)
.update(`${timestamp}.${rawBody}`)
.digest("hex"),
"hex",
);
if (supplied.length !== expected.length || !timingSafeEqual(supplied, expected)) {
throw new Error("Invalid DNSMigrator signature");
}
return timestamp;
}After verification, reject timestamps older or farther in the future than your application permits, and store processed id values if your handler must be idempotent. DNSMigrator signs the timestamp but does not dictate your replay policy.
Delivery safety and failures#
Webhook and Slack endpoints use the same public-address validation and IP pinning as self-hosted providers. A target that resolves to any private or reserved address is blocked. Because notification delivery requests set redirect handling to manual, an HTTP redirect is treated as a failed non-success response rather than followed to another host.
A failed attempt updates both the outbox row and the channel’s last error. Pending deliveries stop after five failed attempts; later successful delivery clears the channel error. Make your endpoint acknowledge accepted work quickly with a success status, perform expensive work asynchronously, and deduplicate on id.
Notification channel creation and deletion are written to the audit log. The current service does not audit pause/resume or test actions; see Activity log for the events that are visible today.