Skip to main content

Worked status-page samples

The operations below document each route’s query and path parameters; this overlay walks the integration an embedder actually ships: read the feed → open one incident’s timeline → optionally parse the RSS → wire up the subscriber flow. The whole surface is unauthenticated — a status page is a public trust signal — so none of the calls need X-API-Key, and every read is edge-cached (s-maxage=60, so a 60-second poll interval is pointless; polls on a one-to-two-minute cadence hit the edge for free). Read and incident-write are split across roles on purpose: anyone with the URL can render the page, while publishing or updating an incident is a tenant-authenticated control-plane action from the dashboard Settings → Platform status surface (the status-page guide walks that console flow). This page documents the public read and subscriber surface — there is no API-key-gated incident-create endpoint to hand to your customers’ browsers. Every JSON response follows the same { data, meta } envelope — data carries the payload, meta carries the request_id and timestamp. Quote meta.request_id when you report a bad payload; support can trace the origin read from it.

1. Render the incident feed

GET /api/v1/public/incidents returns the platform’s declared incidents over a look-back window (days 1–365, default 90), newest first. Filter by component or status when your page needs a shard — a voice-only status strip, a resolved-incidents archive.
200
Empty returns look like {"incidents": []} — render your all-good banner rather than branch-crashing on a null.

2. Open one incident’s timeline

GET /api/v1/public/incidents/{id} returns the same row plus its status-update timeline (newest first, capped at the 100 most recent updates). Follow the lifecycle as it moves investigating → identified → monitoring → resolved — an unknown id returns a plain 404.
200

3. Parse the RSS feed (aggregators / uptime widgets)

GET /api/v1/public/incidents.rss returns the most recent 50 incidents as RSS 2.0 (application/rss+xml). Item titles are prefixed with the current status — [resolved] reads as history, [investigating] as live. Treat the feed as a convenience for readers and polling aggregators; a JSON client should prefer the filtered list op above.

4. Subscribe a reader to incident emails

POST /api/v1/public/statuspage/subscribers starts a double-opt-in flow: the reader (or your embedded widget) submits an email, an out-of-band confirmation email lands, and the body answers with status: "pending_confirmation" and HTTP 202. A repeat subscribe for an already-confirmed address is idempotent — it answers status: "already_subscribed" with HTTP 200 and does not re-email, so a double-clicked widget submit is safe. The route is rate-limited per IP — map a 429 to a retry-after backoff.
202
The confirm link lands at <api-host>/api/v1/public/statuspage/subscribers/confirm/{token} — a GET, because email clients open links with GET. It flips the subscription to confirmed (or answers already_confirmed on a repeat click / unknown token, both as an idempotent success envelope).

5. Render one-click unsubscribe

Every incident notification carries a one-click unsubscribe at GET /api/v1/public/statuspage/subscribers/unsubscribe/{token}. It returns {"status": "unsubscribed"} on the first click and keeps returning the idempotent success {"status": "already_unsubscribed"} on any later click — so your embedded unsubscribe page can render without worrying about the second-tap double-debug. A fresh subscribe re-creates the row cleanly; no manual dedupe work.

Errors worth branching on

These five failures cover the status POST (POST /api/v1/status-page/updates), which every incident flow fires. Each block below is a full { error, meta } envelope as the API returns it, and the matrix at the bottom answers retry vs surface for the same five classes. For the platform-wide decision table these branches plug into, see the error handling guide.

401 — Unauthorized

A 401 on this page is the bearer key failing before the route ran — it never means the resource is wrong. Rotate the key or re-mint the scoped token; retrying the same request changes nothing.

403 — Forbidden

A 403 means the key authenticated but the operation is gated by scope — check the key’s scopes on the developer page; a 403 is never a data-not-found shape.

422 — Schema

A 422 means the payload did not match the request schema — branch on error.details.field and resend with an allowed state value instead of blind-retrying the same body.

429 — Rate

The Retry-After header and error.details.retry_after are both set on every 429 — resend the SAME request after the lower of the two.

404 — Not found

List components first and bind ids on the current page config — a stale id never resolves.

60-second retry matrix