Skip to content

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.

ChannelTargetDelivery format
EmailOne address, or every workspace Owner and Admin when Email address is emptySubject contains the event title and workspace name; body contains the message and optional app link
SlackA Slack incoming webhook at https://hooks.slack.com/…Slack text JSON with a bold title, message and optional Open in DNSMigrator link
WebhookA public HTTPS endpointSigned 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.

EventUI labelEmitted when
migration.previewedPreview readyA migration preview finishes
migration.appliedMigration appliedDestination apply finishes
migration.verifiedMigration verifiedAuthoritative verification finishes
migration.failedMigration needs attentionPreview, apply, verification or rollback fails, or verification finds mismatches
migration.rolled_backMigration rolled backRollback finishes
cutover.stepCutover step finishedA non-final cutover step completes
cutover.completedCutover completeThe cutover workflow reaches done
cutover.failedCutover needs attentionA cutover step pauses as failed
zone.driftZone drift detectedA managed-zone check finds drift
zone.pushedZone changes pushedA managed-zone push succeeds
zone.push_failedZone push failedA managed-zone push finishes with one or more provider failures
backup.failedBackup failedA 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:

text
Content-Type: application/json
User-Agent: DNSMigrator-Webhooks/1
X-DNSMigrator-Event: migration.verified
X-DNSMigrator-Signature: t=UNIX_SECONDS,v1=HEX_HMAC

The raw JSON has this shape:

ts
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.

verify-dnsmigrator-webhook.ts
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.