API

CertWatch's dashboard is a client of its own /v1 HTTP/JSON API. There's no separate public API key today — authenticated routes use the same session cookie the dashboard sets on login, and one route (the certificate checker) is intentionally open with no account required.

Authentication

POST /v1/login and POST /v1/signup set an HttpOnly, SameSite=Lax session cookie good for 30 days from creation (not a sliding window). POST /v1/logout clears it and works even against an already-expired or missing cookie. Forgotten-password flows (/v1/password-reset/request, /v1/password-reset/confirm) don't require a session, for the obvious reason, and never reveal whether a given email has an account.

Endpoints and domains

Session-gated. The shapes mirror what the dashboard shows:

MethodPathWhat it does
GET/v1/endpointsList TLS endpoints, newest first.
POST/v1/endpointsAdd one — {hostname, port, label}, port defaults to 443.
PATCH/v1/endpoints/{id}Partial update — label, hostname/port, active state, or mute.
DELETE/v1/endpoints/{id}Remove one.
GET/v1/endpoints/{id}/checksCheck history, most recent first.
GET/v1/domainsList monitored domains.
POST/v1/domainsAdd one — {name}, a registrable domain like example.com.
GET/v1/accountPlan, limits, and current usage.
PATCH/v1/accountUpdate notification email, Slack/webhook URLs (Pro), or display name.

Adding an endpoint or domain past your plan's limit returns 403 endpoint_limit_reached / 403 domain_limit_reached; a literal duplicate returns 409. Every mutating route is scoped strictly to the caller's own account — another account's resource id 404s rather than 403ing, so a guessed id can't be used to confirm it exists.

Public certificate check

POST /v1/check{host} (a hostname[:port] string, port defaults to 443). No authentication, nothing persisted: runs one TLS check and returns the result plus a computed status (healthy / warning / critical / expired / error), the same thresholds the dashboard uses. Rate-limited per client IP (429 rate_limited, with Retry-After) since it requires no account to attribute requests to.

Billing

POST /v1/billing/checkout and POST /v1/billing/portal return hosted Stripe links for upgrading and for managing an existing subscription (plan changes, cancellation, payment methods) respectively — see Billing for what each plan includes.

Validation and abuse controls

Hostnames and webhook URLs are checked against internal/private address ranges both when you save them and again at dial time (so a hostname that later resolves somewhere internal via DNS rebinding still gets caught) — CertWatch never dials loopback, link-local, RFC 1918/IPv6 unique-local, CGNAT, multicast, or unspecified addresses on your behalf.