Documentation 29
Statuspage quickstart
The outage, as customers were told about it.
An Atlassian Statuspage v1-compatible host for the simulated org. Point any Statuspage client at it with a base-URL swap and read the customer-facing side of a real incident: the components a page publishes, the incidents and scheduled maintenances posted to them, the investigating → identified → monitoring → resolved cadence with per-component status transitions, the published postmortems, and the subscribers. The same incidents are served by the PagerDuty host as status-page posts and sit behind the Sentry errors that opened them.
Coverage, hosts and pinned snapshots for Statuspage — 27 of 38 read surfaces served and verified.
01 / Base URL + auth
One origin, and the OAuth prefix.
Requests go to https://statuspage.sandboxapis.dev. Paths are Statuspage's own — /v1/…, exactly as on api.statuspage.io — so a client that already takes a base URL only changes the origin.
# the org's status pages — one public, one partner-only
curl -H "Authorization: OAuth anything" "https://statuspage.sandboxapis.dev/v1/pages"
# the same call the way Statuspage's own docs write it
curl "https://statuspage.sandboxapis.dev/v1/pages.json?api_key=anything"02 / The walk
Page → components → incidents → updates.
Every step follows a link the previous response handed you. A component's status is live: it reads degraded_performance or major_outage only while an incident that impacts it is still open, and returns to operational when that incident resolves — derived from the incident record rather than stored beside it.
AUTH='-H "Authorization: OAuth anything"'
# 1. the pages, and take the public one's id
PAGE=$(curl -s "https://statuspage.sandboxapis.dev/v1/pages" | jq -r '.[0].id')
# 2. what that page publishes, and how each part is doing right now
curl -s "https://statuspage.sandboxapis.dev/v1/pages/$PAGE/components" \
| jq -r '.[] | "\(.position) \(.status) \(.name)"'
# 3. the incidents — realtime AND scheduled maintenance, which
# Statuspage models as incidents with scheduled_for/scheduled_until
curl -s "https://statuspage.sandboxapis.dev/v1/pages/$PAGE/incidents" \
| jq -r '.[] | "\(.status) \(.impact) \(.name)"'
# 4. one incident's whole cadence, with per-component transitions
curl -s "https://statuspage.sandboxapis.dev/v1/pages/$PAGE/incidents/$INCIDENT" \
| jq '{name, impact, status, updates: [.incident_updates[] |
{created_at, status, body,
moved: [.affected_components[] | "\(.name): \(.old_status) -> \(.new_status)"]}]}'
# 5. the postmortem, where one was published
curl -s "https://statuspage.sandboxapis.dev/v1/pages/$PAGE/incidents/$INCIDENT/postmortem" | jq -r .bodyThe four narrower incident collections are served too and mean what Statuspage means by them: /incidents/unresolved, /incidents/scheduled, /incidents/upcoming and /incidents/active_maintenance.
03 / Paging
Three grammars, including one that starts at zero.
Statuspage pages by offset and sends no Link header — it declares none on any operation, so neither do we. What it does have is three different spellings, and this host mirrors all three rather than normalising them:
# most collections: page + per_page, 1-indexed
curl "https://statuspage.sandboxapis.dev/v1/pages/$PAGE/components?page=1&per_page=100"
# incidents: page + LIMIT, not per_page
curl "https://statuspage.sandboxapis.dev/v1/pages/$PAGE/incidents?page=1&limit=100"
# subscribers: page + limit, and THE FIRST PAGE IS PAGE 0
curl "https://statuspage.sandboxapis.dev/v1/pages/$PAGE/subscribers?page=0&limit=100"The subscribers collection really is zero-indexed — the API documents it as such — and a client that assumes otherwise silently skips its first page. Filters behave the way the rest of this service does: q, type, state and the two sortable fields really filter and sort, and the two sort_field values this universe cannot honour answer a 400 that names the parameter rather than returning rows in the wrong order.
04 / What is not here
27 of 38 read endpoints, and the rest say why.
05 / Where the shapes come from
A published document, in an unusual place.
Atlassian does publish a machine-readable OpenAPI 3.0.0 description of this API, so the claim here is the strong one — our responses validate against the document the provider publishes, not merely against the shape its prose describes. What is unusual is where it lives: there is no standalone spec file anywhere, and the document exists publicly only embedded inside the ReDoc page at developer.statuspage.io. We fetch that page at build time, extract the document, and verify it against a pinned hash — we do not redistribute Atlassian's bytes, because no licence grants it.
Two places where the document and reality disagree, both mirrored toward reality and both written down rather than smoothed over: its error envelope is declared as {"message": …} while the wire sends {"error": …}, and a handful of fields are typed against what the live API returns. Where a field's declared type contradicts its own description, this host omits the field rather than emitting a wrong type — the incident postmortem timestamp is served on the postmortem endpoint, where the document types it correctly.
06 / Pin it for CI
A frozen universe, for tests that must not drift.
The live host rolls forward. For CI, point at the pinned host instead — same paths, same client, an origin that answers the same bytes forever.
curl "https://statuspage-v1-g11.snap.sandboxapis.dev/v1/pages"The generation suffix on a pin name is the universe generation it was frozen on. This host's pages, components, incidents, updates and postmortems all arrived before statuspage-v1-g11 was frozen, so it serves every one of them, and so does the unsubscribed roster above — subscription states arrived with hello-15, which this pin carries. An earlier pin does not, and never will: it answers the 404 that names the generation, permanently, because a pin's bytes never move. How pinned snapshots work →