Webhook Reference

This page covers what a webhook delivery contains and how we send it, for anyone writing an endpoint to receive webhooks. Deliveries are signed following the Standard Webhooks specification. To add a webhook, connect one to a chat or automation app, or troubleshoot its deliveries, see Webhooks.

Payload Formats

Every webhook has a payload format, which you choose under Show advanced options when you add or edit the webhook:

All three carry the same message, which ends with a link to the DNS record in DNS Check:

The example.com A record in the "Web server" record group is failing: https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93/5529

Envelope Format

Every format's message names the record and links to it, but only the Envelope gives your code each detail as its own field, including some the message leaves out, such as why the check failed. The payload is JSON, sent with Content-Type: application/json. The example below is a version 2 envelope, which new webhooks send (see Envelope Versions). It's the same object the Events API returns for the event:

{
  "id": "evt_4f1c9a2e7b3d4c6f8a0e5b2d9c7f1a3e",
  "object": "event",
  "type": "dns_record.failing",
  "timestamp": "2026-09-15T14:05:07Z",
  "notified": true,
  "text": "The example.com A record in the \"Web server\" record group is failing: https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93/5529",
  "data": {
    "dns_record": {
      "id": "9d2e4b7a-1c3f-4e8d-a6b5-0f7c2d9e3a41",
      "object": "dns_record",
      "record_group_id": "3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93",
      "name": "example.com.",
      "type": "A",
      "values": ["192.0.2.10"],
      "web_url": "https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93/5529",
      "api_url": "https://www.dnscheck.co/api/v2/dns-records/9d2e4b7a-1c3f-4e8d-a6b5-0f7c2d9e3a41"
    },
    "record_group": {
      "id": "3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93",
      "object": "record_group",
      "name": "Web server",
      "web_url": "https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93",
      "api_url": "https://www.dnscheck.co/api/v2/record-groups/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93"
    },
    "status": "failing",
    "error": "Expected:\nexample.com. A 192.0.2.10\n\nFound:\nexample.com. A 198.51.100.7",
    "responding_name_server": "198.51.100.53 (ns1.example.net)",
    "history_url": "https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93/5529/history"
  }
}
Field Type Description
id string The event's ID, the same as the webhook-id header: evt_ followed by 32 random hexadecimal characters. It stays the same across retries and across all of your webhooks, so use it to ignore duplicate deliveries.
type string dns_record.failing or dns_record.passing. For an inverted check, dns_record.failing means the unwanted record appeared.
timestamp string When the check that changed the record's state ran, in RFC 3339 format and UTC. It doesn't change when a delivery is retried.
notified boolean Always true in a delivery, since webhooks only deliver events that send notifications. Events in the Events API can have it false.
text string The message, ready to display.
data.dns_record.id string The DNS record's ID, a UUID, the same as in API v2.
data.dns_record.record_group_id string The ID of the record group the record is in, the same as data.record_group.id.
data.dns_record.name string The record's fully qualified domain name, with a trailing dot.
data.dns_record.type string The record type, such as A, MX, or TXT.
data.dns_record.values array of strings The values you're monitoring for, not the values that were found: one, or a load balancer record's several.
data.dns_record.web_url string The record's page in DNS Check.
data.dns_record.api_url string The record in API v2.
data.record_group.id string The record group's ID, a UUID.
data.record_group.name string The record group's name.
data.record_group.web_url string The record group's page in DNS Check.
data.record_group.api_url string The record group in API v2.
data.status string failing or passing, matching type.
data.error string or null Why the check failed, as the Message column of the record's History page shows it, without the suggested fix. It's plain text, sometimes over several lines, such as DNS record does not exist (NXDomain). or the records expected and found. Display it rather than parsing it, since its wording can change. On a passing event, it's usually null, but it can carry a note, such as that the record is flapping.
data.responding_name_server string or null The name server whose answer this check evaluated: its IP address, followed by its hostname in parentheses when it has one, as in the Name server column of the record's History page. Unless the record group sets its own name servers, this is one of DNS Check's resolvers.
data.history_url string The record's History page in DNS Check, which lists its state changes and, for a failure, a suggested fix.

We may add new fields to the envelope in the future, so your endpoint should ignore fields it doesn't recognize. The changelog lists new fields as they're added.

Envelope Versions

The envelope's data comes in two versions, each matching a version of the DNS Check API. Version 2, above, matches API v2 and identifies records by UUID. Version 1 matches API v1, which identifies records by number:

"data": {
  "record": {
    "id": 5529,
    "name": "example.com.",
    "record_type": "A",
    "value": "192.0.2.10",
    "status": "fail",
    "error": "Expected:\nexample.com. A 192.0.2.10\n\nFound:\nexample.com. A 198.51.100.7",
    "responding_name_server": "198.51.100.53 (ns1.example.net)",
    "url": "https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93/5529",
    "history_url": "https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93/5529/history"
  },
  "group": {
    "uuid": "3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93",
    "name": "Web server",
    "url": "https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93"
  }
}

Each field means the same as its version 2 counterpart: record is dns_record, group is record_group, data.record.record_type is data.dns_record.type, data.record.value (a load balancer record's values, comma-separated) is data.dns_record.values, data.group.uuid is data.record_group.id, each url is web_url, and status, error, responding_name_server, and history_url sit inside record. Version 1 has no object or notified field. Its other top-level fields, and the Message Only and Plain Text formats, are the same in both versions.

New webhooks send version 2. Webhooks added before it existed keep sending version 1, which we'll keep sending for as long as API v1 runs. To switch one, edit the webhook, click Show advanced options, and choose the Envelope version. Send a test event to see the new shape before a real event arrives.

Message Only Format

Use this for chat apps that reject unrecognized fields. Enter the key in the Message key field, which defaults to text. With the key set to content, the payload is:

{
  "content": "The example.com A record in the \"Web server\" record group is failing: https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93/5529"
}

Plain Text Format

The message is sent with Content-Type: text/plain; charset=utf-8:

The example.com A record in the "Web server" record group is failing: https://www.dnscheck.co/tests/3f6b2c1e-8d4a-4b7e-9c2f-5a1d7e0b6c93/5529

Request Headers

Every delivery is an HTTPS POST with these headers:

Header Value
Content-Type application/json, or text/plain; charset=utf-8 for the Plain text format.
User-Agent DNS Check https://www.dnscheck.co/contact
webhook-id The event ID, such as evt_4f1c9a2e7b3d4c6f8a0e5b2d9c7f1a3e. The same as the envelope's id.
webhook-timestamp When this delivery attempt was sent, in Unix seconds. Unlike the envelope's timestamp, it's new on every retry.
webhook-signature The delivery's signature, such as v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=. For 24 hours after you roll the secret, it holds two signatures separated by a space. See Verify Webhook Signatures.
X-DNSCheck-Event The event type, dns_record.failing or dns_record.passing, so you can route a delivery without parsing its payload.
X-DNSCheck-Test true, on test events only. Real events don't include it.
Your custom header If you've set one. See Custom Header.

Verify Signatures

Anyone who finds out your webhook's URL can post to it, so check each delivery's signature before acting on it. See Verify Webhook Signatures for examples with and without a Standard Webhooks library, and for how to roll a webhook's signing secret.

Custom Header

If your endpoint expects a credential in a header, such as a bearer token or an API key, add it as the webhook's custom header, under Show advanced options when you add or edit the webhook. We send the header's name and value exactly as you enter them, with every delivery. For example:

Header name Header value
Authorization Bearer your-token
X-API-Key your-api-key

We never show the saved value again. When you edit the webhook, leave the value blank to keep it, or enter a new one to replace it. To remove the header, clear its name.

You can't use headers we set ourselves, such as Content-Type, User-Agent, and the webhook- signing headers.

Basic Authentication

For an endpoint that uses HTTP Basic authentication, set the header name to Authorization and the value to Basic followed by a space and the base64 encoding of username:password. To encode them:

echo -n 'username:password' | base64

Tokens in the URL

Some services, such as Discord and Google Chat, put the token in the webhook URL itself. We support these URLs without modification. When an endpoint accepts a token either in the URL or in a header, use the header: URLs are often written to the endpoint's access logs and to any proxy logs along the way, while header values usually aren't.

Delivery and Retries

Responding to a Delivery

Respond with any 2xx status code to confirm a delivery. We wait up to 3 seconds to connect and 15 seconds for a response.

We don't follow redirects, and a 3xx response is a failed delivery. The most common cause is a URL with a missing or extra trailing slash that the endpoint redirects to its preferred form, so use the exact URL your endpoint expects. Google Apps Script web apps always respond with a redirect after running, so their deliveries show as failed even though the script ran.

Retries

Response What happens
2xx Delivered.
5xx, 408, 423, 425, 429, a timeout, or any other error without an HTTP response, such as Host not found or a TLS error Retried, with the wait between attempts doubling each time (1 minute, then 2, 4, 8, and so on) up to 4 hours. If the response has a Retry-After header asking for a longer wait, we wait that long instead. We stop retrying after 24 hours, or sooner if a Retry-After asks us to wait past then.
Any other 4xx, a 3xx, or a blocked destination Not retried. Fix the problem, and later events will be delivered.

Catching Up with the Events API

API v2's event feed lists every event on your account, each the same object as its version 2 envelope, so the code that handles a delivery can handle an event from the feed. To catch up after an outage longer than 24 hours, read the feed with since set to a time a little before the outage began, and skip the webhook-ids your endpoint already processed, as the Events API page's Following the Feed describes.

The feed also lists events that sent no notifications, with notified: false (see When Events Are Sent). Webhooks never deliver those, so skip them if your endpoint should act only on what it would have received.

Repeated Failures

One failed delivery, or an afternoon of them, doesn't affect the webhook. What counts is a run of deliveries we've given up on: a response we don't retry, such as a 404, counts on its first attempt, while 5xx responses and timeouts count only once their 24 hours of retries run out. After 3 of these in a row, we email you that the webhook is failing. A failed test event doesn't count.

We disable the webhook and email you again when a failure meets both of these conditions:

Both emails name the webhook and the last result we got from it. They go to the account holder and to any team members with the Admin role.

The Webhooks tab flags a failing webhook with , and one disabled this way with . Once the endpoint is working again, choose Enable from its actions menu, then Send test event to confirm it. Enabling clears the failure count, and so does any successful delivery, including a test.

Duplicates and Ordering

A delivery can arrive more than once. For example, if your endpoint processes a delivery but responds after the 15-second timeout, we retry it. Every copy carries the same webhook-id header, in every payload format, so record the IDs you've processed and skip ones you've already seen. Retries stop soon after 24 hours, so keeping each ID for 2 days is enough.

Deliveries can also arrive out of order: if a dns_record.failing delivery is retried, the dns_record.passing event that followed it might arrive first. When order matters, compare the envelope's timestamp with the last one you've processed for the same data.dns_record.id, and ignore older events. Only the Envelope format includes these fields, so use it if your endpoint keeps track of each record's state.

IP Addresses

We send deliveries from the same servers we use to query name servers. If your endpoint sits behind a firewall, allow the IPv4 and IPv6 addresses listed in the FAQ. They're also published as the A and AAAA records of nameservers.dnscheck.co, which you can look up with dig nameservers.dnscheck.co A and dig nameservers.dnscheck.co AAAA.

We don't send deliveries to private, loopback, or other non-public addresses, so the webhook hostname must resolve to a public IP address.


DNS monitoring illustration

Protect your DNS infrastructure with automated monitoring

Get notified immediately when DNS records change. Start monitoring your critical DNS infrastructure for free in under 5 minutes.

No credit card required • Cancel anytime