# Watch a domain's registration expiry (Domain expiry monitor)

The **Domain expiry** monitor (API type `domainExp`, shown in the app as **Domain expiration**) looks up a domain's
registration and reminds you before it lapses. A missed renewal takes your site, email and every subdomain offline
at once, however healthy your servers are - this monitor catches it weeks ahead.

:::tip[Usually attached]
If the domain already has a Website or API monitor, switch on **Domain Expiration** there instead - see
[Attach sub-checks to a monitor](/monitors/types/attached-sub-checks/).
:::

## At a glance

| | |
|---|---|
| API type token | `domainExp` |
| Runs from | HostTracker's own internal check network - no location picker |
| Schedule | every 6 hours, fixed |
| Lookup | RDAP first, WHOIS as a fallback; one lookup is shared by every monitor watching the same domain |
| Plan gates | the Domain expiry type is a package feature (`domainExp`) |

## How it decides

- **Reminders**: when the domain has exactly **30**, **7** or **1** day left, the monitor sends an informational
  reminder to its contacts subscribed to **Up** (not voice-call contacts), at most once a day per threshold.
- **Renewal notice**: when the registry reports a new expiration date, the same contacts get a notice with the old
  and new dates.
- **Down**: when the expiration date has passed, or the registry says the domain is not registered.
- A registry that times out or errors is simply asked again at the next check; it never produces a Down on its own.

## Settings reference

A Domain expiry monitor has no type-specific settings. The name, tags, **Full Log**, **Open Stats** and
subscriptions work as described in [Common monitor fields](/monitors/types/http/#common-monitor-fields).

| Setting (app label) | API field | Type / allowed values | Default | Plan limits | What it does for you |
|---|---|---|---|---|---|
| **Domain** | `url` | a registered domain name | required | - | The domain to watch. A URL is reduced to its host and a leading `www.` is removed. IP addresses and single-label names are refused. |
| Interval | `interval` | ignored | 6 hours | - | Fixed; a sent value is replaced and the response carries a warning. Cron is not supported either. |
| Locations | `locations` | not accepted | - | - | Sending pools is refused. |

The reminder days are fixed at 30, 7 and 1.

## Set it up in the app

1. On the **Sites** dashboard, click **Add Monitor** and choose **Domain expiration** in **Monitoring Type**.
2. Enter the registered domain (for example `example.com`) in **Domain** and a name.
3. Check **Alert Subscriptions**: reminders go to contacts subscribed to **Up**, expiry to those subscribed to
   **Down**.
4. Click **Save**.

## Do it with the API or MCP

```bash
curl -X POST https://api2.host-tracker.com/monitor \
  -H "Authorization: Bearer $HT_TOKEN" -H "Content-Type: application/json" \
  -d '{ "type": "domainExp", "url": "example.com", "name": "example.com registration" }'
```

Update the domain or name with `PATCH /monitor/{id}`, for example `{"name": "Main domain"}`.

MCP: `create_monitor(type="domainExp", url="example.com")` - no `interval`, no `pools`.

Read the current expiry with `GET /monitor/{monitorId}/attached` - the `domainExp` block carries `expiresAt`
(Unix seconds), `daysLeft`, `domain`, `sharedDomain` and `checkedAt` - or `get_monitor(id, expand="attached")`.
Webhooks receive `domain.expiring` on each reminder.

## What happens next

The monitor shows the expiration date and days left after its first lookup, then refreshes every 6 hours. After
you renew, the new date appears at the next lookup and a renewal notice is sent.

## Limits and gotchas

- Enter the registered domain, not a subdomain: the lookup uses the host as written, and registries generally
  know only registered domains. This also applies to the attached check, which takes the parent monitor's host.
- Some top-level domains publish no expiration date; the monitor then stays Up without reminders.
- `403 package_limit` - your package does not include Domain expiry monitors.
- `422 validation_failed` with `reason: pool_not_supported_for_type` - omit `locations`.

## Related

- [Attach sub-checks to a monitor](/monitors/types/attached-sub-checks/)
- [Certificate expiry monitor](/monitors/types/ssl-expiry/)
- [DNS lookup check](/monitors/types/dns/)
