Founding keys are opena year free at 6,000 req/hour, for the first 1,000 developersClaim yours

Statuspage

Point a Statuspage client at https://statuspage.sandboxapis.dev — paths are /v1/… exactly as on api.statuspage.io, so a caller keeps writing /v1/pages/{page_id}/incidents unchanged. Auth is 'Authorization: OAuth <anything>' (the prefix is OAuth, NOT Bearer — that is Statuspage's own scheme), and no token at all works too; the ?api_key= query param the docs describe is accepted as well. A .json suffix on any path is tolerated, as the real API's own examples use. WHAT IS RENDERED: the customer-facing status story, end to end — the org's two status pages (one public, one partner-only), the components each page publishes, every incident and scheduled maintenance posted to them, each incident's full update cadence (investigating -> identified -> monitoring -> resolved) with per-component old_status/new_status transitions, the published postmortems, and the page's subscribers with their counts and by-state histogram. THE INCIDENTS ARE THE SAME OUTAGES sentry./pd. serve the responder-facing half of: one canonical incident, the same timestamps, the same affected services — a Statuspage incident and a PagerDuty status-page post are one event in two dialects. Component status is LIVE and derived from the incident record: a component reads degraded_performance or major_outage only while a post that impacts it is still open. WHAT IS NOT: component groups, metrics and metrics providers, uptime calculations, page access users and groups, incident templates and the status embed config — this universe carries no canonical fact behind any of them, so each answers an explicit coverage 404 rather than an invented value or an empty list that would assert the fact is absent rather than unmodelled. ONE GENERATION NOTE: the unsubscribed roster IS served, and it needs generation hello-15 — the generation that gave this universe a subscription state meaning "unsubscribed" at all. Live rides hello-19, which inherits it, and answers it; so do the statuspage-v1-g11 pin on hello-16 and the statuspage-v1-g10 pin on hello-15 itself; a pin frozen earlier answers a 404 naming the generation rather than an empty list that would read as "nobody ever unsubscribed". Check https://sandboxapis.dev/providers/statuspage for the row-by-row picture.

REST · 38 endpointsMCP-ready

Coverage badge

Deep37/38 · 97%

The share of read rows with a final answer — served and verified, a deviation, retired upstream, or a reviewed empty/refusal. Computed in the coverage manifest, copied here.

Live host
statuspage.sandboxapis.dev
Pinned hosts
3 — generation 9, 10, 11
Serving since
2026-09-06
Lifecycle
Live

Served & verified

27 / 71%

Answering with real universe data, each response checked against Statuspage's published spec by the conformance suite on this build. 1 of them is marked a deviation — served faithfully, but failing the vendored spec.

The full read API

38

Every read surface Statuspage publishes, deferred long tail included, minus the rows excluded by policy. Writes are out of scope: this universe is read-only.

Not served yet

11

Each returns an explicit, provider-shaped coverage error naming the gap — never invented data.

01 / The swap

Point your client at a different base URL.

No SDK of ours, no shim, no recorded fixtures. The same client library you already use, one environment variable different.

shell
curl "https://statuspage.sandboxapis.dev/v1/pages"

Verified drop-in clients

No client library is version-pinned against Statuspage yet, so this page claims none. What is asserted is the wire: every response above is validated against Statuspage's own published spec by the conformance suite. Client pins live in coverage/client-pins.yaml and are added as each SDK joins the suite.

02 / Coverage by family

Every read surface, grouped the way Statuspage groups it.

All 38 rows the coverage manifest carries for Statuspage, deferred long tail included and nothing capped. Open a family, or filter by path to find the exact endpoint your client calls.

Status — what a conformance test found

served & verified
answers with real universe data, and this build checked that response against the vendor's spec.
deviation
served and faithful to the real provider, but failing the vendored spec — usually a bug in the spec.
retired
the vendor removed the endpoint; snapshots pinned before that date still serve it.
planned / deferred
not served yet — an explicit coverage error naming the gap, never invented data.
excluded
out of the claim by policy (writes, and surfaces we refuse); not in any denominator on this page.

Mode — what kind of answer a row gets

derive
the response is computed from artifact rows that already exist
generate
canon does not carry this yet; the generator will produce it, then derive
empty
the true answer for this universe is an empty collection — reason + reviewed date required
refuse
mirror the provider's OWN refusal (e.g. its 403 for a non-admin token) — reason + reviewed date required
read-only
a write named in the manifest because clients probe it; the read-only 403 IS its final response, and it never joins the badge denominator

A row with no mode shown has not been judged yet. Modes are the manifest's own words, from coverage/MODES.yaml; every empty and refuse carries a written reason and a review date before it counts as final.

Badge — where Deep starts

Full
every published read row has a final answer
Deep
60% up to 100%
Partial
25% up to 60%
Preview
under 25%

REST + GraphQL + git rows that are not `excluded`. Write operations are NOT rows (DECISIONS 2026-09-01 decision 9): they are counted in meta.write_operations and never enter this ratio.

38 read surfaces in 14 families

incidents6 of 6 served & verified
GET
/pages/{page_id}/incidents
served & verified
Core
GET
/pages/{page_id}/incidents/{incident_id}
served & verified
Core
GET
/pages/{page_id}/incidents/active_maintenance
served & verified
Core
GET
/pages/{page_id}/incidents/scheduled
served & verified
Core
GET
/pages/{page_id}/incidents/unresolved
served & verified
Core
GET
/pages/{page_id}/incidents/upcoming
served & verified
Core
subscribers5 of 5 served & verified
GET
/pages/{page_id}/subscribers
served & verified
Next
GET
/pages/{page_id}/subscribers/{subscriber_id}
served & verified
Next
GET
/pages/{page_id}/subscribers/count
served & verified
Next
GET
/pages/{page_id}/subscribers/histogram_by_state
served & verified
Next
GET
/pages/{page_id}/subscribers/unsubscribed

StatusPageSubscription.status carries "unsubscribed" as of hello-15 (features.subscriptionStates, the founder's 2026-09-06 ruling), with unsubscribed_epoch for the instant. This serves exactly the rows subscribed() excludes from the list, the count, the histogram and the by-id lookup, because Statuspage's own state filter is closed at active/unconfirmed/quarantined and gives a departed subscriber their own collection instead of a fourth state. A pre-hello-15 artifact answers the generation gap, not [], which would assert that nobody in a universe that cannot represent unsubscribing ever did

served & verified
Next
page-access-users1 of 4 served & verified
GET
/pages/{page_id}/page_access_users

THIS PAGE IS NOT AUDIENCE-SPECIFIC, and the fact rests on something a caller can already read on this same host rather than on an assertion. The vendored document says it in its own tag prose, twice and in the same words - "You will need Statuspage to switch your page type to audience-specific before these API endpoints have any effect on your status page" - and shapePage() publishes viewers_must_be_team_members off canon's status_page.kind, which is Statuspage's TEAM-MEMBERS-ONLY page and a different type from an audience-specific one. Canon carries exactly two page kinds, public and private, and no third. So nobody logs in to see this status page. The ONE person-to-page link canon does hold is status_page_subscription.person, and it is a NOTIFICATION RECIPIENT off the engineering roster rather than a login-holding viewer - which is exactly why the subscriber rows on this host serve real rows while this one is empty, and the conformance entry asserts BOTH halves on one run so a later wave cannot make the two agree by habit rather than by evidence. Statuspage's own answer for a page with none is [] at 200. RULED by the founder 2026-09-14 on the unjudged-47 pass's proposal, as proposed.

served & verified
Long tail
GET
/pages/{page_id}/page_access_users/{page_access_user_id}

refuse — ENTAILED by the empty beside it: the page-access-user list serves [] on this same host, so no page access user id resolves and Statuspage's own 404 is the whole answer. RULED by the founder 2026-09-14 on the unjudged-47 pass's proposal, as proposed.

deferred
Long tail
GET
/pages/{page_id}/page_access_users/{page_access_user_id}/components

refuse — ENTAILED by the same empty: no page access user resolves, so no per-user component list exists to serve. RULED by the founder 2026-09-14 on the unjudged-47 pass's proposal, as proposed.

deferred
Long tail
GET
/pages/{page_id}/page_access_users/{page_access_user_id}/metrics

refuse — ENTAILED TWICE OVER, and both halves are worth stating because either alone would carry it: no page access user resolves, AND /pages/{page_id}/metrics is itself a ruled honest empty on this host (2026-09-12, no metrics provider is connected to this page), so even a user who did resolve could own no metric. RULED by the founder 2026-09-14 on the unjudged-47 pass's proposal, as proposed.

deferred
Long tail
component-groups1 of 3 served & verified
GET
/pages/{page_id}/component-groups

this universe has no component group at all - status_page_service carries no group column and renderer-statuspage/src/render.ts already says so in as many words while omitting `group_id` from every served component; Statuspage's own answer for a page with no groups is `[]` at 200. RULED by the founder 2026-09-12 (round 19), on the coverage audit's proposal

served & verified
Next
GET
/pages/{page_id}/component-groups/{id}

refuse — ENTAILED by the empty beside it: the group list serves `[]` on this same host, so no id resolves and Statuspage's own 404 is the whole answer. The body is the document's own declared 404 description in the captured `{"error": ...}` envelope - Statuspage's 404 can never be captured (its auth layer answers 401 before its router), so an authored sentence would be a claim this repository cannot check, and `x-sandboxapis-refusal` is the whole wire difference. RULED by the founder 2026-09-12 (round 19), on the coverage audit's proposal

deferred
Next
GET
/pages/{page_id}/component-groups/{id}/uptime

refuse — ENTAILED by the same empty: with no group, there is no group to compute uptime over. Same body and same header as the row above. RULED by the founder 2026-09-12 (round 19), on the coverage audit's proposal

deferred
Next
components3 of 3 served & verified
GET
/pages/{page_id}/components
served & verified
Core
GET
/pages/{page_id}/components/{component_id}
served & verified
Core
GET
/pages/{page_id}/components/{component_id}/uptime

SERVED 2026-09-12 (wave W6): the component's uptime, COMPUTED from the page's own incident record rather than stored - the same walk shapeIncidentUpdate already makes to resolve old_status/new_status, with a clock attached: the seconds between consecutive reported_epoch instants are seconds that component spent in the status the earlier update gave it. DOWNTIME IS BOTH KINDS OF OUTAGE, and the rule was chosen for a reason the caller can check: Statuspage publishes uptime_percentage, major_outage and partial_outage and no formula relating them, and its own example (96.67 with partial_outage 0) cannot discriminate - so the rule taken is the one under which the object is SELF-CONSISTENT, and a caller can recompute the percentage from the two second-counts and the range this same response publishes. degraded_performance is not an outage and contributes nothing. The default window is the document's own ninety days ending at the ARTIFACT'S ANCHOR - 'today in the page's time zone' for a frozen universe is the instant it is frozen at - and `start`/`end` are honoured as the partial dates the document describes, with its own clamp warning when an end date is past today and its documented 422 for a range over six calendar months. `related_events` is OMITTED: the document types it as a single object carrying one `id` while describing it as 'Related incidents', and three of this page's posts touch the fixture component - emitting one would be picking and an array would fail the document's own schema. range_start/range_end are full ISO date-times because the field's `format: date-time` is what a generated client enforces, though the example beside it prints a bare date

served & verified
Core
metrics1 of 3 served & verified
GET
/pages/{page_id}/metrics

this status page publishes no metric - Statuspage requires every published metric to belong to a metrics provider and this page has none (the row below); canon's metric_series and sample rows belong to the internal observability canon (metric_series.service names a TECHNICAL service) and no status_page_service publishes one. NOTE the document types this collection as a single `Metric` where the live API returns a list - the sixth of the seven bugs coverage/provider-pins.yaml catalogues for this provider - so the array is the mirror and `[]` is its empty shape either way. RULED by the founder 2026-09-12 (round 19), on the coverage audit's proposal

served & verified
Long tail
GET
/pages/{page_id}/metrics_providers/{metrics_provider_id}/metrics

refuse — ENTAILED by the same empty: no provider id resolves, so there is no provider whose metrics could be listed. RULED by the founder 2026-09-12 (round 19), on the coverage audit's proposal

deferred
Long tail
GET
/pages/{page_id}/metrics/{metric_id}

refuse — ENTAILED by the empty above: the page publishes no metric, so no metric id resolves and Statuspage's own 404 is the whole answer, in the document's own declared 404 description. RULED by the founder 2026-09-12 (round 19), on the coverage audit's proposal

deferred
Long tail
page-access-groups1 of 3 served & verified
GET
/pages/{page_id}/page_access_groups

THIS PAGE IS NOT AUDIENCE-SPECIFIC, and the fact rests on something a caller can already read on this same host rather than on an assertion. The vendored document says it in its own tag prose, twice and in the same words - "You will need Statuspage to switch your page type to audience-specific before these API endpoints have any effect on your status page" - and shapePage() publishes viewers_must_be_team_members off canon's status_page.kind, which is Statuspage's TEAM-MEMBERS-ONLY page and a different type from an audience-specific one. Canon carries exactly two page kinds, public and private, and no third. So this page has no page access group - not because the generator forgot a roster, but because the page has no audience to roster. Statuspage's own answer for a page with none is [] at 200. THE PAGE STILL HAS TO RESOLVE: an id this universe does not have is the ordinary entity 404, never a cheerful empty. RULED by the founder 2026-09-14 on the unjudged-47 pass's proposal, as proposed; this SUPERSEDES wave W6's framing of these rows as a canon question.

served & verified
Long tail
GET
/pages/{page_id}/page_access_groups/{page_access_group_id}

refuse — ENTAILED by the empty beside it: the group list serves [] on this same host, so no group id resolves and Statuspage's own 404 is the whole answer - the component-groups precedent this fence ruled on 2026-09-12, one family over. The body is the document's own declared 404 description in the captured {"error": ...} envelope, and x-sandboxapis-refusal is the whole wire difference. RULED by the founder 2026-09-14 on the unjudged-47 pass's proposal, as proposed.

deferred
Long tail
GET
/pages/{page_id}/page_access_groups/{page_access_group_id}/components

refuse — ENTAILED by the same empty: with no page access group there is no group whose components could be listed. Same body and same header as the row above. RULED by the founder 2026-09-14 on the unjudged-47 pass's proposal, as proposed.

deferred
Long tail
incident-subscribers2 of 2 served & verified
GET
/pages/{page_id}/incidents/{incident_id}/subscribers

the canon models subscription scope on a real column (subscribable_kind is one of status_page / status_page_service / status_page_post) and the renderer queries it. As of hello-15 (features.subscriptionStates) this universe EMITS post-scoped subscriptions, so the row answers non-empty rather than the [] the 2026-09-01 note flagged. The founder confirmed derive over empty on 2026-09-06

served & verified
Next
GET
/pages/{page_id}/incidents/{incident_id}/subscribers/{subscriber_id}

SERVED 2026-09-12 (wave W6): one post-scoped subscriber, addressed by id. THE ROUTE WAS ALREADY REGISTERED AND SERVING - its list twin .../subscribers has been `derive` since 2026-09-06 and this by-id row simply had no MODES entry - so the work was a manifest entry plus a conformance assertion. The lookup is scoped to the POST, which is what makes it a different operation from /pages/{page_id}/subscribers/{subscriber_id}: a page-scoped subscriber does not resolve here, and conformance asserts it

served & verified
Next
metric-providers1 of 2 served & verified
GET
/pages/{page_id}/metrics_providers

no metrics provider is connected to this page - canon carries no Datadog/Pingdom/New Relic connection and no self-hosted provider row, and without a provider Statuspage can publish no metric at all; its own answer for a page with none is `[]` at 200. RULED by the founder 2026-09-12 (round 19), on the coverage audit's proposal

served & verified
Long tail
GET
/pages/{page_id}/metrics_providers/{metrics_provider_id}

refuse — ENTAILED by the empty beside it: the provider list serves `[]`, so no provider id resolves. Statuspage's own 404, not the 403 the same operation also declares: that 403 is about a CALLER's authorization, and this ruling is about an object nobody created. RULED by the founder 2026-09-12 (round 19), on the coverage audit's proposal

deferred
Long tail
pages2 of 2 served & verified
GET
/pages
served & verified
Core
GET
/pages/{page_id}
served & verified
Core
users1 of 2 served & verified · 1 deviation
GET
/organizations/{organization_id}/users
served & verified
Long tail
GET
/organizations/{organization_id}/permissions/{user_id}

SERVED 2026-09-12 (wave W6): one member's access across the organization's pages - the SAME user set /organizations/{id}/users serves (org members who are not bots) and both real page ids, so a caller cannot find somebody in one list and be told they do not exist in the other. THE THREE RBAC BOOLEANS ARE OMITTED ON THE DOCUMENT'S OWN SENTENCE: page_configuration, incident_manager and maintenance_manager each carry the description 'This field will only be present if the organization has Role Based Access Control', and this canon models none - emitting them would assert an RBAC organization. STATUS `deviation`, NOT `green`, and the reason is the document rather than us: it types `data.pages` as a single OBJECT and prints an ARRAY of two page entries in its own example three lines below. This organization really has two pages, so one object could not carry the answer; the row mirrors the live API and the example, not the declared type, which is what `deviation` means. The eighth document bug catalogued for this provider

deviation
Long tail
incident-postmortem1 of 1 served & verified
GET
/pages/{page_id}/incidents/{incident_id}/postmortem
served & verified
Next
incident-templates1 of 1 served & verified
GET
/pages/{page_id}/incident_templates

SERVED 2026-09-12 (wave W6): the universe's own three status-update templates - reader.listStatusUpdateTemplates(), the same three renderer-pagerduty publishes, so one canonical template is one template on both hosts. `name` is the label the Templates tab shows and `title` the subject line canon stores; `update_status` is emitted for the two slots that name a Statuspage status exactly (the holding statement is `investigating`, the resolution notice says in its own text that the incident is resolved) and OMITTED for the progress update, whose message renders {{incident.status}} rather than setting one. THE BODY KEEPS ITS PLACEHOLDERS, and that is the wave's one ruling here: canon's template text carries the field keywords template_field publishes, Statuspage's own templates have no interpolation, and resolving is not available because a template is attached to no incident and no component - so there is no incident number and no business service to resolve against. Omitting the body would leave a template with no content; the text is served verbatim and the deviation from Statuspage's plain-text templates is recorded. components/group_id/should_tweet/should_send_notifications are omitted: canon's template names no service, belongs to no group, and this universe has no Twitter integration

served & verified
Long tail
status-embed-config0 of 1 served & verified
GET
/pages/{page_id}/status_embed_config

generate — A WIDENING, NOT A BUILD, and the one of the eight unjudged Statuspage rows that is NOT the audience question. A status embed config is a page's own PRESENTATION settings - `position`, the corner the iframe sits in, and four colours for the incident and maintenance states - and canon's status_page table carries six columns of which none is presentational (packages/artifact/src/rows.ts:1122: id, kind, name, url, published_epoch, content_seed). Real Statuspage answers 200 here for every page, so neither cheap answer is available: there is no collection to be empty, and a 404 would deny an operation Statuspage serves. The document names no default for any of the five fields and publishes no example, so nothing is derivable and nothing may be authored - the placeholder invariant. The widening is five columns on status_page, themed at compile time like every other page string. It unblocks ONE row, on this provider only: no other host in the fleet publishes an embed configuration. CLASSIFIED 2026-09-13 (the unjudged-47 pass); the 2026-09-12 W6 fence grouped it with the seven page-access rows as "one canon question", and it is a different one

deferred
Long tail

03 / What's simulated

One data set, rendered in Statuspage's dialect.

What another provider has to agree with, and the test that makes it

Each line below is one assertion in the conformance suite named beside it — run on every build, over one artifact, through the real renderers. Nothing is claimed here that no expect checks.

04 / Known deviations

1 row is served but do not validate.

These answer with real data and match what Statuspage actually returns, but fail the spec we vendor from Statuspage — nearly always a bug in that published description. They are listed rather than counted so you can see whether one of them is the endpoint your client calls, and they are never counted as verified.

05 / Pinned snapshots

Frozen universes, on their own hostnames.

Each pin regenerates byte-identically on every request, so a test written against one never drifts. Generations are DIFFERENT universes, not versions of one — never swap a suffix expecting the same data.

PinHostUniverse generationStatuspage API versionRepository files
statuspage-v1-g10statuspage-v1-g10.snap.sandboxapis.devgeneration 10v1served
statuspage-v1-g11statuspage-v1-g11.snap.sandboxapis.devgeneration 11v1served
statuspage-v1-g9statuspage-v1-g9.snap.sandboxapis.devgeneration 9v1served

Need an endpoint that is not served yet?

Every row Statuspage's manifest carries is on this page, so “not here” is an answer rather than a gap in the rendering. Tell us which path and which client, and it moves up the queue — the order is set by what people ask for.

Request coverage