Developer reference

Domain Watchlist

DomScan API: Monitor domains for expiration and availability changes. Get alerts when registered domains become available.

Domain Watchlist

Monitor domains for expiration and availability changes. Get alerts when registered domains become available.

Monitor

Sign in with Google to access your personalized domain watchlist with email alerts, webhooks, and more.

Receive notifications via Email, Telegram, Slack, Discord, or Webhook.

GET /v1/watchlist

Get all domains in your watchlist.

Paginated: send limit (1 to 100) and pass next_cursor back as cursor. Responses include has_more and next_cursor. Without limit the whole list is returned. See pagination.

Example response

{
                "watchlist": [
                {
                "domain": "startup.com",
                "added_at": "2024-01-15T10:30:00Z",
                "status": "registered",
                "expiry_date": "2025-01-15T00:00:00Z",
                "days_until_expiry": 287,
                "last_checked": "2024-03-01T12:00:00Z",
                "notify_email": "[email protected]",
                "check_count": 4
                }
                ],
                "total": 1,
                "updated_at": "2024-03-01T12:00:00Z"
                }

Response fields

Field Type
has_more boolean
next_cursor string | null
watchlist[] object[]
watchlist[] object
watchlist[].domain string
watchlist[].added_at string
watchlist[].status string
watchlist[].last_checked string
watchlist[].expiry_date string
watchlist[].days_until_expiry integer
watchlist[].notify_email string
watchlist[].check_count integer
total integer
updated_at string
POST /v1/watchlist

Add a domain to your watchlist.

Request body

{
                "domain": "mydomain.com",
                "notify_email": "[email protected]"
                }

Response fields

Field Type
success boolean
message string
watchlist[] object[]
watchlist[] object
watchlist[].domain string
watchlist[].added_at string
watchlist[].status string
watchlist[].last_checked string
watchlist[].expiry_date string
watchlist[].days_until_expiry integer
watchlist[].notify_email string
watchlist[].check_count integer
total integer

Example response

{
  "success": true,
  "message": "Added example.org to watchlist",
  "watchlist": [
    {
      "domain": "example.com",
      "added_at": "2026-06-02T09:12:00.000Z",
      "last_checked": "2026-10-09T03:00:12.000Z",
      "status": "expiring_soon",
      "expiry_date": "2026-12-18T14:22:08Z",
      "days_until_expiry": 70,
      "notify_email": "[email protected]"
    },
    {
      "domain": "example.org",
      "added_at": "2026-10-09T10:15:30.000Z",
      "status": "unknown",
      "notify_email": "[email protected]"
    }
  ],
  "total": 2
}
DELETE /v1/watchlist?domain=mydomain.com

Remove from watchlist

curl -X DELETE -H "X-API-Key: your-api-key" "https://domscan.net/v1/watchlist?domain=mydomain.com"

Response fields

Field Type
success boolean
message string
domain string
watchlist[] object[]
watchlist[] object
watchlist[].domain string
watchlist[].added_at string
watchlist[].status string
watchlist[].last_checked string
watchlist[].expiry_date string
watchlist[].days_until_expiry integer
watchlist[].notify_email string
watchlist[].check_count integer
total integer

Example response

{
  "success": true,
  "message": "Request completed",
  "domain": "example.com",
  "watchlist": [
    {
      "domain": "example.com",
      "added_at": "2026-08-27T12:00:00Z",
      "status": "registered",
      "last_checked": "2026-08-27T12:00:00Z",
      "expiry_date": "2026-08-27T12:00:00Z",
      "days_until_expiry": 1,
      "notify_email": "[email protected]",
      "check_count": 1
    }
  ],
  "total": 1
}
GET /v1/watchlist/expiring?days=30

Expiring soon

Paginated: send limit (1 to 100) and pass next_cursor back as cursor. Responses include has_more and next_cursor. Without limit the whole list is returned. See pagination.

Example response

{
                "expiring_domains": [
                {
                "domain": "renewal-window.com",
                "status": "expiring_soon",
                "expiry_date": "2026-05-12T00:00:00Z",
                "days_until_expiry": 28,
                "check_count": 11
                }
                ],
                "total": 1,
                "threshold_days": 30
                }

Response fields

Field Type
has_more boolean
next_cursor string | null
expiring_domains[] object[]
expiring_domains[] object
expiring_domains[].domain string
expiring_domains[].added_at string
expiring_domains[].status string
expiring_domains[].last_checked string
expiring_domains[].expiry_date string
expiring_domains[].days_until_expiry integer
expiring_domains[].notify_email string
expiring_domains[].check_count integer
total integer
threshold_days integer
GET /v1/watchlist/available

Available

Paginated: send limit (1 to 100) and pass next_cursor back as cursor. Responses include has_more and next_cursor. Without limit the whole list is returned. See pagination.

Example response

{
                "available_domains": [
                {
                "domain": "startupdrop.ai",
                "status": "available",
                "last_checked": "2026-04-14T08:30:00Z",
                "check_count": 18
                }
                ],
                "total": 1,
                "message": "These domains from your watchlist are now available for registration!"
                }

Response fields

Field Type
has_more boolean
next_cursor string | null
available_domains[] object[]
available_domains[] object
available_domains[].domain string
available_domains[].added_at string
available_domains[].status string
available_domains[].last_checked string
available_domains[].expiry_date string
available_domains[].days_until_expiry integer
available_domains[].notify_email string
available_domains[].check_count integer
total integer
message string

Alert webhooks

Receive monitoring alerts as signed JSON POST requests on your own HTTPS endpoints. Manage endpoints with an API key, or from Monitor settings while signed in.

Event types

MonitorEvents
Watchlist availability, expiry and statusdomain.available domain.expiring_30 domain.expiring_14 domain.expiring_7 domain.expiring_1 domain.status_change
Domain change monitoringdomain.dns_changed domain.certificate_changed domain.registration_changed domain.hosting_changed domain.technology_changed domain.email_security_changed
Brand monitoring (new lookalike registrations)brand.threat_detected
Watchlist alert subscriptions (certificates, prices, DNS records, lookalikes)ssl.expiring ssl.expired ssl.changed price.dropped dns.changed typosquat.detected
Async jobs (free, only for jobs created with notify_webhook)job.completed job.failed job.cancelled
Test event (free)webhook.test

Read the webhook alerts API guide

Pricing

Each alert delivered to an endpoint with a 2xx response costs 2 credits. Credits are reserved just before the first attempt and refunded if the delivery fails, is cancelled or the endpoint is deleted. Test events are free. When the balance is too low, the alert is skipped and nothing is charged.

POST /v1/webhooks

Register an HTTPS endpoint. The URL must resolve to a public address. Omit events, or pass ["*"], to receive every event type. The signing secret is returned only in this response.

Example request

curl -X POST "https://domscan.net/v1/webhooks" \
  -H "X-API-Key: $DOMSCAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.example.com/domscan",
    "events": ["domain.expiring_30", "domain.available", "domain.dns_changed"]
  }'

Example response

{
  "endpoint": {
    "id": "whe_3f1c0a9b8e7d6c5b4a39281706f5e4d3",
    "url": "https://hooks.example.com/domscan",
    "events": ["domain.available", "domain.expiring_30", "domain.dns_changed"],
    "status": "active",
    "secret_hint": "9c2e"
  },
  "secret": "whsec_..."
}

Response fields

Field Type
endpoint object
endpoint.id string
endpoint.url string
endpoint.description string | null
endpoint.events[] string[]
endpoint.status string
endpoint.disabled_reason string | null
endpoint.consecutive_failures integer
endpoint.secret_hint string
endpoint.previous_secret_expires_at string | null
endpoint.secret_rotated_at string | null
endpoint.last_success_at string | null
endpoint.last_failure_at string | null
endpoint.last_error string | null
endpoint.created_at string
endpoint.updated_at string
endpoint.disabled_at string | null
secret string
GET /v1/webhooks

List your endpoints with their status, subscribed events, failure count and the last four characters of the secret, together with the available event types and the delivery policy.

Response fields

Field Type
endpoints[] object[]
endpoints[] object
endpoints[].id string
endpoints[].url string
endpoints[].description string | null
endpoints[].events[] string[]
endpoints[].status string
endpoints[].disabled_reason string | null
endpoints[].consecutive_failures integer
endpoints[].secret_hint string
endpoints[].previous_secret_expires_at string | null
endpoints[].secret_rotated_at string | null
endpoints[].last_success_at string | null
endpoints[].last_failure_at string | null
endpoints[].last_error string | null
endpoints[].created_at string
endpoints[].updated_at string
endpoints[].disabled_at string | null
available_events[] string[]
all_events string
test_event string
policy object
policy.secret_rotation_grace_seconds integer
policy.secret_rotation_credits integer
policy.credits_per_delivered_alert integer
policy.test_event_credits integer
policy.job_event_credits integer
policy.failed_deliveries_charged boolean
policy.max_endpoints integer
policy.max_attempts integer
policy.disable_after_consecutive_failures integer
policy.signature_tolerance_seconds integer

Example response

{
  "endpoints": [
    {
      "id": "example-id",
      "url": "https://example.com",
      "events": [
        "example"
      ],
      "status": "active",
      "secret_hint": "example",
      "created_at": "2026-08-27T12:00:00Z",
      "description": "example",
      "disabled_reason": "customer",
      "consecutive_failures": 1,
      "previous_secret_expires_at": "2026-08-27T12:00:00Z",
      "secret_rotated_at": "2026-08-27T12:00:00Z",
      "last_success_at": "2026-08-27T12:00:00Z",
      "last_failure_at": "2026-08-27T12:00:00Z",
      "last_error": "example",
      "updated_at": "2026-08-27T12:00:00Z",
      "disabled_at": "2026-08-27T12:00:00Z"
    }
  ],
  "available_events": [
    "domain.available"
  ],
  "policy": {
    "credits_per_delivered_alert": 2,
    "test_event_credits": 0,
    "failed_deliveries_charged": false,
    "max_endpoints": 1,
    "max_attempts": 1,
    "disable_after_consecutive_failures": 1,
    "signature_tolerance_seconds": 1,
    "secret_rotation_grace_seconds": 86400,
    "secret_rotation_credits": 0,
    "job_event_credits": 0
  },
  "all_events": "*",
  "test_event": "webhook.test"
}
PATCH /v1/webhooks/:webhook_id

Pause or enable an endpoint, change its events or edit its description. Enabling an endpoint resets its failure count.

{ "enabled": true, "events": ["*"] }

Response fields

Field Type
endpoint object
endpoint.id string
endpoint.url string
endpoint.description string | null
endpoint.events[] string[]
endpoint.status string
endpoint.disabled_reason string | null
endpoint.consecutive_failures integer
endpoint.secret_hint string
endpoint.previous_secret_expires_at string | null
endpoint.secret_rotated_at string | null
endpoint.last_success_at string | null
endpoint.last_failure_at string | null
endpoint.last_error string | null
endpoint.created_at string
endpoint.updated_at string
endpoint.disabled_at string | null

Example response

{
  "endpoint": {
    "id": "example-id",
    "url": "https://example.com",
    "events": [
      "example"
    ],
    "status": "active",
    "secret_hint": "example",
    "created_at": "2026-08-27T12:00:00Z",
    "description": "example",
    "disabled_reason": "customer",
    "consecutive_failures": 1,
    "previous_secret_expires_at": "2026-08-27T12:00:00Z",
    "secret_rotated_at": "2026-08-27T12:00:00Z",
    "last_success_at": "2026-08-27T12:00:00Z",
    "last_failure_at": "2026-08-27T12:00:00Z",
    "last_error": "example",
    "updated_at": "2026-08-27T12:00:00Z",
    "disabled_at": "2026-08-27T12:00:00Z"
  }
}
DELETE /v1/webhooks/:webhook_id

Delete an endpoint. Pending deliveries are cancelled and any reserved credits are refunded.

Response fields

Field Type
id string
deleted boolean

Example response

{
  "id": "example-id",
  "deleted": true
}
POST /v1/webhooks/:webhook_id/test

Send one free webhook.test event and return the result. Test events are not retried and do not count toward automatic pausing.

Response fields

Field Type
delivery object
delivery.id string
delivery.endpoint_id string
delivery.event_id string
delivery.event_type string
delivery.status string
delivery.attempts integer
delivery.next_attempt_at string | null
delivery.last_attempt_at string | null
delivery.response_status integer | null
delivery.last_error string | null
delivery.credits_charged integer
delivery.refunded boolean
delivery.created_at string
delivery.delivered_at string | null
delivered boolean

Example response

{
  "delivery": {
    "id": "example-id",
    "endpoint_id": "example",
    "event_id": "example",
    "event_type": "domain",
    "status": "pending",
    "attempts": 1,
    "next_attempt_at": "2026-08-27T12:00:00Z",
    "last_attempt_at": "2026-08-27T12:00:00Z",
    "response_status": 1,
    "last_error": "example",
    "credits_charged": 1,
    "refunded": false,
    "created_at": "2026-08-27T12:00:00Z",
    "delivered_at": "2026-08-27T12:00:00Z"
  },
  "delivered": false
}
POST /v1/webhooks/:webhook_id/rotate-secret

Replace the signing secret of an endpoint, including a paused one. The new secret is returned only in this response. For the next 24 hours each delivery is signed with both the new and the previous secret. Rotating is free.

Example request

curl -X POST "https://domscan.net/v1/webhooks/whe_3f1c0a9b8e7d6c5b4a39281706f5e4d3/rotate-secret" \
  -H "X-API-Key: $DOMSCAN_API_KEY"

Example response

{
  "endpoint": {
    "id": "whe_3f1c0a9b8e7d6c5b4a39281706f5e4d3",
    "status": "active",
    "secret_hint": "4b7a",
    "previous_secret_expires_at": "2026-10-11T12:00:00.000Z"
  },
  "secret": "whsec_...",
  "previous_secret_expires_at": "2026-10-11T12:00:00.000Z",
  "grace_period_seconds": 86400
}

Response fields

Field Type
endpoint object
endpoint.id string
endpoint.url string
endpoint.description string | null
endpoint.events[] string[]
endpoint.status string
endpoint.disabled_reason string | null
endpoint.consecutive_failures integer
endpoint.secret_hint string
endpoint.previous_secret_expires_at string | null
endpoint.secret_rotated_at string | null
endpoint.last_success_at string | null
endpoint.last_failure_at string | null
endpoint.last_error string | null
endpoint.created_at string
endpoint.updated_at string
endpoint.disabled_at string | null
secret string
previous_secret_expires_at string
grace_period_seconds integer
GET /v1/webhooks/:webhook_id/deliveries

Query parameters

ParameterTypeDescription
limit integer Number of deliveries to return, from 1 to 100. Default 50.

List recent deliveries with their status, attempts, HTTP response, credits charged and refunds. Delivery records are kept for 30 days.

Response fields

Field Type
endpoint_id string
deliveries[] object[]
deliveries[] object
deliveries[].id string
deliveries[].endpoint_id string
deliveries[].event_id string
deliveries[].event_type string
deliveries[].status string
deliveries[].attempts integer
deliveries[].next_attempt_at string | null
deliveries[].last_attempt_at string | null
deliveries[].response_status integer | null
deliveries[].last_error string | null
deliveries[].credits_charged integer
deliveries[].refunded boolean
deliveries[].created_at string
deliveries[].delivered_at string | null

Example response

{
  "endpoint_id": "example",
  "deliveries": [
    {
      "id": "example-id",
      "endpoint_id": "example",
      "event_id": "example",
      "event_type": "domain",
      "status": "pending",
      "attempts": 1,
      "next_attempt_at": "2026-08-27T12:00:00Z",
      "last_attempt_at": "2026-08-27T12:00:00Z",
      "response_status": 1,
      "last_error": "example",
      "credits_charged": 1,
      "refunded": false,
      "created_at": "2026-08-27T12:00:00Z",
      "delivered_at": "2026-08-27T12:00:00Z"
    }
  ]
}

Request format

Each alert is a POST request with a JSON body. The id field stays the same for an event and is repeated in X-DomScan-Event-Id, so you can ignore retries you have already processed.

POST /domscan HTTP/1.1
Content-Type: application/json
X-DomScan-Event: domain.expiring_30
X-DomScan-Event-Id: evt_6f1d2c3b4a5948e7b6c5d4e3f2a1b0c9
X-DomScan-Timestamp: 1791633600
X-DomScan-Signature: sha256=...

{
  "id": "evt_6f1d2c3b4a5948e7b6c5d4e3f2a1b0c9",
  "type": "domain.expiring_30",
  "created": "2026-10-10T12:00:00.000Z",
  "data": {
    "domain": "acme.io",
    "previous_status": "registered",
    "current_status": "expiring_soon",
    "expiry_date": "2026-11-09T00:00:00Z",
    "days_until_expiry": 30,
    "checked_at": "2026-10-10T12:00:00.000Z"
  }
}

Verify signatures

Compute an HMAC-SHA256 of the X-DomScan-Timestamp value, a period and the raw request body using your endpoint secret. Compare the result with X-DomScan-Signature in constant time and reject requests whose timestamp is more than 5 minutes old.

The sample below checks every signature in the header, so it keeps working during a secret rotation.

import crypto from 'node:crypto';

// rawBody: the request body exactly as received, before JSON parsing.
function verifyDomScanWebhook(rawBody, headers, secret) {
  const timestamp = headers['x-domscan-timestamp'];
  const header = headers['x-domscan-signature'] || '';
  if (!/^\d{10}$/.test(timestamp || '')) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = Buffer.from('sha256=' + crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + rawBody)
    .digest('hex'));
  // One entry normally; two during a secret rotation (new secret first).
  return header.split(',').some((entry) => {
    const candidate = Buffer.from(entry.trim());
    return candidate.length === expected.length &&
      crypto.timingSafeEqual(candidate, expected);
  });
}

Rotate a signing secret

During the 24 hour grace period after a rotation, X-DomScan-Signature holds two signatures separated by a comma, with the new secret first, as shown below. Accept a request when any entry matches a secret you trust, deploy the new secret, then stop trusting the old one. Rotating again during the grace period retires the older previous secret at once.

X-DomScan-Signature: sha256=<new>,sha256=<previous>

Retries and automatic pausing

Return any 2xx status within 8 seconds to acknowledge an alert. Redirects, other status codes and timeouts are retried with exponential backoff (1, 4, 16, 64 and 256 minutes), up to 6 attempts in total. After 15 consecutive failed attempts the endpoint is paused; enable it again with PATCH or from Monitor settings.

When an endpoint is paused after repeated failures, the owner of the endpoint receives one email with the endpoint host, the last error and a link to Monitor settings. Fix the endpoint, then enable it again from Monitor settings or with PATCH and enabled set to true. Enabling resets the failure count to zero.

Watchlist alert subscriptions

Subscribe a watched item to the alerts you want. Each subscription watches one hostname, TLD or brand domain, and you choose which of its events to receive. Events go to your signed webhook endpoints and to your in-app alerts.

TypeEventsWhen it fires
sslssl.expiring ssl.expired ssl.changedssl.expiring when the certificate a hostname serves expires within ssl_expiring_days (30, 14, 7 or 1, default 14), once per certificate and threshold. ssl.expired once per expired certificate. ssl.changed when a new certificate fingerprint is seen on two consecutive checks; the payload says whether the issuer changed.
priceprice.droppedWhen the registration or renewal price of a TLD, at one registrar or the cheapest one, falls by at least drop_percent from the highest price since the last alert, or reaches target_price or less. Prices come from the daily pricing snapshot, and each snapshot is evaluated once.
dnsdns.changedWhen a watched record type (A, AAAA, MX, NS, TXT or CNAME) of any hostname returns the same new answer on two consecutive confirmed lookups.
typosquattyposquat.detectedWhen a lookalike of the brand domain is newly registered, confirmed by an authoritative registration lookup. The first full pass over the lookalikes sets the baseline and does not alert.

Only exact evidence triggers an event. A failed TLS handshake, a DNS lookup that fails or times out, a missing price and a lookup that is blocked or rate limited are unknown: they change nothing and never alert or charge. The first confirmed check of a subscription sets its baseline.

Checks run automatically: certificates every 12 hours, DNS records every hour (with a confirming check 15 minutes after a change is first seen), prices every 2 hours against the daily snapshot, and lookalikes in batches of 30 with a full pass every day. After an unknown check the next attempt comes sooner.

Creating subscriptions and running checks is free. Each alert delivered to a webhook endpoint with a 2xx response costs 2 credits, like every other monitoring alert. Failed deliveries are refunded, and an account can have up to 100 subscriptions, 20 of them for lookalikes.

POST /v1/watchlist/alerts

Create a subscription. type decides which fields apply: domain for ssl, dns and typosquat; tld, registrar, price_type, drop_percent and target_price for price. Fields that do not apply to the type are rejected.

Request body

ParameterTypeDescription
typestringssl, price, dns or typosquat. Required.
domainstringHostname to watch for ssl and dns, or the brand domain for typosquat.
tldstringTLD to watch for price, for example com.
registrarstringRegistrar id for price. Omit it to watch the cheapest registrar.
eventsstring[]Events to receive. Defaults to every event of the type.
ssl_expiring_daysintegerDays before expiry for ssl.expiring: 30, 14, 7 or 1. Default 14.
price_typestringregistration (default) or renewal.
drop_percentnumberAlert on a drop of at least this percentage, 1 to 95.
target_pricenumberAlert when the price in USD reaches this amount or less. A price subscription needs drop_percent, target_price or both.
record_typesstring[]Record types to watch for dns. Defaults to A, AAAA, MX, NS, TXT and CNAME.

Example request

curl -X POST "https://domscan.net/v1/watchlist/alerts" \
  -H "X-API-Key: $DOMSCAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "price",
    "tld": "com",
    "registrar": "porkbun",
    "price_type": "renewal",
    "drop_percent": 10,
    "target_price": 9.5
  }'

Example response

{
  "subscription": {
    "id": "mas_8c1f0e2d3b4a59687766554433221100",
    "type": "price",
    "target": "com",
    "registrar": "porkbun",
    "events": ["price.dropped"],
    "status": "active",
    "price_type": "renewal",
    "drop_percent": 10,
    "target_price": 9.5,
    "currency": "USD",
    "observed": null,
    "last_check_status": null,
    "next_check_at": "2026-10-10T12:00:00.000Z"
  }
}

Response fields

Field Type
subscription object
subscription.id string
subscription.type string
subscription.target string
subscription.registrar string | null
subscription.events[] string[]
subscription.status string
subscription.ssl_expiring_days integer | null
subscription.price_type string | null
subscription.drop_percent number | null
subscription.target_price number | null
subscription.currency string | null
subscription.record_types[] string[]
subscription.observed object | null
subscription.last_check_at string | null
subscription.last_check_status string | null
subscription.last_error string | null
subscription.next_check_at string | null
subscription.created_at string
subscription.updated_at string
GET /v1/watchlist/alerts

List your subscriptions with their latest confirmed evidence and check status, the events each type supports, the check schedule and the alert price.

Response fields

Field Type
subscriptions[] object[]
subscriptions[] object
subscriptions[].id string
subscriptions[].type string
subscriptions[].target string
subscriptions[].registrar string | null
subscriptions[].events[] string[]
subscriptions[].status string
subscriptions[].ssl_expiring_days integer | null
subscriptions[].price_type string | null
subscriptions[].drop_percent number | null
subscriptions[].target_price number | null
subscriptions[].currency string | null
subscriptions[].record_types[] string[]
subscriptions[].observed object | null
subscriptions[].last_check_at string | null
subscriptions[].last_check_status string | null
subscriptions[].last_error string | null
subscriptions[].next_check_at string | null
subscriptions[].created_at string
subscriptions[].updated_at string
total integer
available_types object
available_types.ssl[] string[]
available_types.price[] string[]
available_types.dns[] string[]
available_types.typosquat[] string[]
policy object
policy.credits_per_delivered_alert integer
policy.check_credits integer
policy.unknown_checks_charged boolean
policy.max_subscriptions integer
policy.max_typosquat_subscriptions integer
policy.ssl_expiring_days[] integer[]
policy.default_ssl_expiring_days integer
policy.record_types[] string[]
policy.price_types[] string[]
policy.check_interval_minutes object
policy.confirmation_checks object

Example response

{
  "subscriptions": [
    {
      "id": "example-id",
      "type": "ssl",
      "target": "example",
      "registrar": "example",
      "events": [
        "ssl.expiring"
      ],
      "status": "active",
      "ssl_expiring_days": 30,
      "price_type": "registration",
      "drop_percent": 0.5,
      "target_price": 0.5,
      "currency": "USD",
      "record_types": [
        "A"
      ],
      "observed": {},
      "last_check_at": "2026-08-27T12:00:00Z",
      "last_check_status": "baseline",
      "last_error": "example",
      "next_check_at": "2026-08-27T12:00:00Z",
      "created_at": "2026-08-27T12:00:00Z",
      "updated_at": "2026-08-27T12:00:00Z"
    }
  ],
  "total": 1,
  "available_types": {
    "ssl": [
      "ssl.expiring"
    ],
    "price": [
      "price.dropped"
    ],
    "dns": [
      "dns.changed"
    ],
    "typosquat": [
      "typosquat.detected"
    ]
  },
  "policy": {
    "credits_per_delivered_alert": 2,
    "check_credits": 0,
    "unknown_checks_charged": false,
    "max_subscriptions": 1,
    "check_interval_minutes": {},
    "max_typosquat_subscriptions": 1,
    "ssl_expiring_days": [
      1
    ],
    "default_ssl_expiring_days": 1,
    "record_types": [
      "A"
    ],
    "price_types": [
      "registration"
    ],
    "confirmation_checks": {}
  }
}
GET /v1/watchlist/alerts/:subscription_id

Get one subscription with its latest confirmed evidence and its 20 most recent events.

Response fields

Field Type
subscription object
subscription.id string
subscription.type string
subscription.target string
subscription.registrar string | null
subscription.events[] string[]
subscription.status string
subscription.ssl_expiring_days integer | null
subscription.price_type string | null
subscription.drop_percent number | null
subscription.target_price number | null
subscription.currency string | null
subscription.record_types[] string[]
subscription.observed object | null
subscription.last_check_at string | null
subscription.last_check_status string | null
subscription.last_error string | null
subscription.next_check_at string | null
subscription.created_at string
subscription.updated_at string
events[] object[]
events[] object
events[].id string
events[].event_type string
events[].detected_at string
events[].published_at string | null
events[].data object

Example response

{
  "subscription": {
    "id": "example-id",
    "type": "ssl",
    "target": "example",
    "registrar": "example",
    "events": [
      "ssl.expiring"
    ],
    "status": "active",
    "ssl_expiring_days": 30,
    "price_type": "registration",
    "drop_percent": 0.5,
    "target_price": 0.5,
    "currency": "USD",
    "record_types": [
      "A"
    ],
    "observed": {},
    "last_check_at": "2026-08-27T12:00:00Z",
    "last_check_status": "baseline",
    "last_error": "example",
    "next_check_at": "2026-08-27T12:00:00Z",
    "created_at": "2026-08-27T12:00:00Z",
    "updated_at": "2026-08-27T12:00:00Z"
  },
  "events": [
    {
      "id": "example-id",
      "event_type": "ssl.expiring",
      "detected_at": "2026-08-27T12:00:00Z",
      "published_at": "2026-08-27T12:00:00Z",
      "data": {}
    }
  ]
}
PATCH /v1/watchlist/alerts/:subscription_id

Pause or resume a subscription with enabled, or change its events, ssl_expiring_days, drop_percent, target_price or record_types. A new target price only alerts on a later price movement.

{ "enabled": false }

Response fields

Field Type
subscription object
subscription.id string
subscription.type string
subscription.target string
subscription.registrar string | null
subscription.events[] string[]
subscription.status string
subscription.ssl_expiring_days integer | null
subscription.price_type string | null
subscription.drop_percent number | null
subscription.target_price number | null
subscription.currency string | null
subscription.record_types[] string[]
subscription.observed object | null
subscription.last_check_at string | null
subscription.last_check_status string | null
subscription.last_error string | null
subscription.next_check_at string | null
subscription.created_at string
subscription.updated_at string

Example response

{
  "subscription": {
    "id": "example-id",
    "type": "ssl",
    "target": "example",
    "registrar": "example",
    "events": [
      "ssl.expiring"
    ],
    "status": "active",
    "ssl_expiring_days": 30,
    "price_type": "registration",
    "drop_percent": 0.5,
    "target_price": 0.5,
    "currency": "USD",
    "record_types": [
      "A"
    ],
    "observed": {},
    "last_check_at": "2026-08-27T12:00:00Z",
    "last_check_status": "baseline",
    "last_error": "example",
    "next_check_at": "2026-08-27T12:00:00Z",
    "created_at": "2026-08-27T12:00:00Z",
    "updated_at": "2026-08-27T12:00:00Z"
  }
}
DELETE /v1/watchlist/alerts/:subscription_id

Delete a subscription. Events it has not sent yet are dropped and never charged.

Response fields

Field Type
id string
deleted boolean

Example response

{
  "id": "example-id",
  "deleted": true
}

Example dns.changed event

{
  "id": "evt_2b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e",
  "type": "dns.changed",
  "created": "2026-10-10T12:15:00.000Z",
  "data": {
    "domain": "partner-shop.example",
    "changes": [
      {
        "record_type": "MX",
        "before": ["10 mx1.mail.example"],
        "after": ["10 mx.other-mail.example"],
        "added": ["10 mx.other-mail.example"],
        "removed": ["10 mx1.mail.example"],
        "first_seen_at": "2026-10-10T12:00:00.000Z"
      }
    ],
    "confirmed_checks": 2,
    "confirmed_at": "2026-10-10T12:15:00.000Z",
    "subscription_id": "mas_8c1f0e2d3b4a59687766554433221100"
  }
}