---
title: "Alerts"
method: GET
path: "/alerts"
tags: ["monitoring"]
---

# Alerts

`GET /alerts`

## Request

- Base URL: `https://dnsdoctor.dev/api/v1`
- URL: `https://dnsdoctor.dev/api/v1/alerts`
- Auth: HTTP bearer

The token account's monitoring alert log, newest first. Token required.

Filters: `since` (an INCLUSIVE ISO-8601 `created_at` floor — poll by storing
the newest `created_at` seen and passing it back; rows repeat rather than go
missing, so dedupe on `id`; 422 on an unparseable value, and any valid ISO
rendering works — it is normalized to the stored UTC form before comparing),
`domain` (must be one of the caller's verified domains, else the same opaque
404 the rest of the surface gives — ownership is never leaked), `type` (422 on
an unknown value, never a silent empty page), `limit` (1..100), `before` (the
opaque `created_at,id` keyset cursor from a previous page's `next_before`,
422 malformed).

**Page down before advancing `since`.** `next_before` is non-null exactly
when older rows remain in this window; a caller that ignores it, takes a full
page and moves its watermark to the newest row it saw drops every row it did
not receive — silently, which is the failure this feed exists to prevent.

**READ-ONLY, and that is a product decision, not an omission**: there is no
ack, no delete, and no mutation of any kind here. Acking is the human's own
triage state on their dashboard — an agent that acks on their behalf silences
a row the human has never seen, which is precisely the failure this feed
exists to prevent. Both surfaces read the same rows; only the human clears
them (design D87 §Part 1).

Every row carries `delivery_class`, so an agent sees the rows the D86 tiering
routed `dashboard_only` — the ones deliberately kept out of the digest mail.
An agent watching only the inbox would miss them; that log parity is the point
of the endpoint.

No plan gate, deliberately: read-only-degrade (D42) keeps alert history
readable on a lapsed plan exactly as the session-authed `/monitor/alerts` and
the per-domain history reads do. What lapsing stops is scans, alerts and new
RUA ingestion — never a read of data the account already has.

## Query parameters

- `since` string, nullable
- `domain` string, nullable
- `type` string, nullable
- `limit` integer
- `before` string, nullable

## Response `200`

Successful Response

- AlertsOut: The `GET /v1/alerts` response: newest-first rows for the token's account. `next_before` is the opaque `created_at,id` keyset cursor for the next (older) page, or null when this page is the last one. It is NOT decoration: without it a caller that polls with `since` and receives a full page has no way to know rows were cut off, and advancing its watermark to the newest row it saw skips every older row in that window permanently — the one thing this feed exists not to do. Page down with `before` until `next_before` is null, THEN advance `since`. Same format as the session-authed log's cursor (one encoder, `api/monitor.py::encode_alert_cursor`).
  - `alerts` AlertOut[], required
    - `id` integer, required
    - `domain` string, required
    - `type` string, required
    - `check` string, nullable, required
    - `summary` string, required
    - `detail` object, required
    - `created_at` string, required
    - `email_sent_at` string, nullable, required
    - `acknowledged_at` string, nullable, required
    - `delivery_class` string, nullable, required
    - `kind` string, required
  - `next_before` string, nullable, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/dnsdoctor/apis/dns-doctor-api.md) · [All operations](https://skmtc.dev/dnsdoctor/apis/dns-doctor-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dnsdoctor/apis/dns-doctor-api/revisions/1d0114806e76?raw)
