1. Documentation
  2. HTTP API
  3. Subscriber endpoints
ReadmenpmGitHub
  • Introduction
  • Quickstart
  • Configuration file
  • Monitors
  • Checks and scheduling
  • Notifications
  • Incidents and maintenance
  • Subscribers
  • Metrics and badges
  • Identity and appearance
  • Telemetry
  • The check run
  • Uptime and history
  • Automatic incidents
  • Notification delivery
  • Public subscriptions
  • StatusPage component
  • Themes
  • UI primitives
  • Colour mode
  • React Router
  • Next.js
  • Astro
  • SQLite
  • Cloudflare D1
  • PostgreSQL
  • Schema and migrations
  • Cloudflare Workers
  • Vercel
  • Self-hosting
  • API overview
  • Status endpoints
  • Incident and maintenance endpoints
  • Subscriber endpoints
  • Badges, feeds and metrics
  • Admin endpoints
  • CLI commands
  • Package exports
  • Security
  • Troubleshooting

Subscriber endpoints

Create, confirm and remove public subscriptions.

Every route requires subscribers.enabled; otherwise they answer 404 { "error": "This feature is disabled." }. They are unauthenticated, which is the whole point — and the reason for the protections documented in Public subscriptions.

POST /subscribers

bash
curl -X POST https://status.acme.com/api/heyo-status/subscribers \  -H "content-type: application/json" \  -d '{"type":"email","target":"person@example.com"}'
type"email" | "webhook"required

Must be listed in subscribers.methods, otherwise 400 { "error": "The webhook subscription method is disabled." }. rss is not a stored subscription.

targetstringrequired

An email address, or an HTTP(S) URL for webhooks.

monitorsstring[] | nulloptional

Optional filter. null or omitted means every monitor.

ResponseBodyWhen
202{ "status": "pending" }Email, confirmation required.
201{ "status": "subscribed" }Confirmation not required.
400{ "error": "…" }Invalid type, target or method disabled.
503{ "error": "…" }Email confirmation could not be sent.

The response is identical whether the destination was new or already subscribed. Anyone can submit someone else's address, so the subscription id — an unsubscribe capability — and the confirmation state are never revealed.

Target validation

Email targets must match a basic address shape. Webhook targets must be HTTP(S), carry no credentials, and resolve to a publicly routable literal — loopback, private ranges, link-local and cloud metadata, carrier-grade NAT, multicast, IPv4-mapped IPv6 and reserved suffixes such as .local and .internal are all rejected:

json
{  "error": "target must be a public host. Loopback, private and link-local addresses are rejected."}

Idempotency

A subscription is identified by its destination. Repeating the request returns the same public response without sending another email. A pending confirmation is reused for 15 minutes; after that the pending row is replaced so the new token is valid.

Concurrent requests for the same destination are settled by a unique index: the loser reports the winner's subscription instead of an error.

GET /subscribers/confirm

bash
curl "https://status.acme.com/api/heyo-status/subscribers/confirm?token=9f8e…"
ResponseBody
200the subscriber record
400{ "error": "token is required." }
404{ "error": "Confirmation token is invalid or expired." }
json
{  "id": "sub_5d31ae",  "type": "email",  "target": "person@example.com",  "confirmed": true,  "monitors": null,  "createdAt": "2026-01-01T09:00:00.000Z"}

Tokens are single use. The response sets Cache-Control: no-store and Referrer-Policy: no-referrer so the token is not leaked onwards.

GET /subscribers/:id/unsubscribe

Returns a small, self-contained HTML page with a confirm button. It is noindex, no-store and depends on none of the application's assets, so it still works when the rest of the deployment does not.

A link that unsubscribed on GET would fire whenever a mail scanner prefetched it, which is why the destructive step is a POST.

POST /subscribers/:id/unsubscribe

The form target of that page. Returns HTML: 200 when removed, 404 when the subscription is already gone.

DELETE /subscribers/:id

The programmatic equivalent.

bash
curl -X DELETE https://status.acme.com/api/heyo-status/subscribers/sub_5d31ae
ResponseMeaning
204Removed.
404Unknown id.
The id is a capability

Knowing a subscription id is enough to remove it. That is intentional — unsubscribing must never require an account — so treat the id as a secret and never expose it in a listing or a log.

Listing subscribers

There is no HTTP route that lists subscriptions, deliberately: it would be an address book of your visitors behind one token. Read them in-process when you genuinely need to:

ts
const subscribers = await statusPage.database.listSubscribers({ confirmed: true });

Related

  • Double opt-in and SSRF protections
  • Enabling subscriptions
  • Email channels
Incident and maintenance endpoints< PreviousBadges, feeds and metricsNext >

Powered by heyo

On this page

POST /subscribersTarget validationIdempotencyGET /subscribers/confirmGET /subscribers/:id/unsubscribePOST /subscribers/:id/unsubscribeDELETE /subscribers/:idListing subscribers