Point a PagerDuty API client at https://pd.sandboxapis.dev — paths are root-level (/incidents, /services, /oncalls), exactly like api.pagerduty.com, with no version prefix: PagerDuty versions in the Accept header. The official PagerDuty MCP server takes PAGERDUTY_API_HOST; pdpyras takes PDSession(url=...); the Go and Node SDKs take a base-URL option. Auth is 'Authorization: Token token=<key>' (any value is accepted here). This is the org's ON-CALL surface: services are what the org deploys, escalation policies and schedules decide who is woken, and an incident's log entries are the real timeline — alert fired, page sent, page acknowledged, fix resolved. ONE THING TO KNOW: GET /incidents defaults to the last month (PagerDuty's own default) and this universe's incident is older, so pass date_range=all.
Coverage badge
Full211/211 · 100%
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.
pd.sandboxapis.devServed & verified
184 / 87%
Answering with real universe data, each response checked against PagerDuty's published spec by the conformance suite on this build. 5 of them are marked a deviation — served faithfully, but failing the vendored spec.
The full read API
211
Every read surface PagerDuty publishes, deferred long tail included, minus the rows excluded by policy. Writes are out of scope: this universe is read-only.
Not served yet
27
Each returns an explicit, provider-shaped coverage error naming the gap — never invented data.
01 / The swap
No SDK of ours, no shim, no recorded fixtures. The same client library you already use, one environment variable different.
curl -H 'Authorization: Token token=anything' "https://pd.sandboxapis.dev/incidents?date_range=all"Verified drop-in clients
No client library is version-pinned against PagerDuty yet, so this page claims none. What is asserted is the wire: every response above is validated against PagerDuty'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
All 211 rows the coverage manifest carries for PagerDuty, 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
Mode — what kind of answer a row gets
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 Full starts
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.
211 read surfaces in 29 families
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — PagerDuty's own words on every operation in this family: "This API is in Early Access and may change at any time. Contact your PagerDuty account team to request access." This replica has not requested access and cannot — the gate is a conversation with a vendor, not a plan — so PagerDuty's refusal to an unenrolled account IS the true answer. Serving a Contextual Data Platform schema would additionally assert an integration posture (ServiceNow CMDB sync) that this engineering org has never had
refuse — account-administration machinery this replica does not model and should not simulate: seat licensing and plan allocation are a BILLING relationship, and this canon models an engineering organization rather than a customer of PagerDuty. There is no canonical row that could answer without inventing a plan for olympus-labs — the exact objection the 2026-09-01 /abilities refusal raised and the capture later answered for THAT row only. PagerDuty's own 4xx is the true answer here, not our coverage 404
refuse — live authentication machinery, not engineering-org data: an inventory of the OAuth tokens and browser sessions currently issued against the account is a security surface whose truthful content changes by the minute and whose simulated content would be a set of credentials that authenticate nothing. The canon records no session and no token, and a replica that invented one would be publishing credential-shaped strings
refuse — live authentication machinery, not engineering-org data: an inventory of the OAuth tokens and browser sessions currently issued against the account is a security surface whose truthful content changes by the minute and whose simulated content would be a set of credentials that authenticate nothing. The canon records no session and no token, and a replica that invented one would be publishing credential-shaped strings
refuse — live authentication machinery, not engineering-org data: an inventory of the OAuth tokens and browser sessions currently issued against the account is a security surface whose truthful content changes by the minute and whose simulated content would be a set of credentials that authenticate nothing. The canon records no session and no token, and a replica that invented one would be publishing credential-shaped strings. This operation is ALSO `deprecated: true` upstream with a documented replacement (the List OAuth Delegations endpoint) — a retirement candidate recorded in the review doc, deliberately NOT armed in the gateway registry here because PagerDuty has published no removal date
refuse — live authentication machinery, not engineering-org data: an inventory of the OAuth tokens and browser sessions currently issued against the account is a security surface whose truthful content changes by the minute and whose simulated content would be a set of credentials that authenticate nothing. The canon records no session and no token, and a replica that invented one would be publishing credential-shaped strings. This operation is ALSO `deprecated: true` upstream with a documented replacement (the List OAuth Delegations endpoint) — a retirement candidate recorded in the review doc, deliberately NOT armed in the gateway registry here because PagerDuty has published no removal date
hello-13's ScheduleOverride canon: two people covered somebody else's shift, and `replaces` names the person the rotation formula produces across the window so an override can never disagree with the schedule it overrides
the v3 rendering of the same two ScheduleOverride rows as OverrideShift; rotation_id and custom_shift_id are omitted rather than invented, this canon has neither entity
one override by id, byte-identical to the row the v3 list served; an id this rotation never issued is a 404
the document supports exactly one feature here (`aiops`) and says an unentitled account gets a warning alongside the data. NO AIOps name appears in the 53-name captured /abilities response this replica serves, so `enabled: true` would contradict the list published one path over (invariant #5) — the entitlement is READ from our own ability list rather than chosen. The document leaves `enabled` unstated for the unentitled case and `false` is the only reading consistent with "not entitled to use"; the ambiguity is recorded in DECISIONS 2026-09-02 [pagerduty/enablements]. `updated_at` is omitted because nobody has ever changed a setting nobody can change
NULL, and the null is the fact rather than a gap: a global impact threshold is something an admin SETS, setting one is a write, and the document's own example for an account that has not set one is `{"global_threshold": null}`. Naming a priority would assert an impact rule this account never configured — and every incident here would then read as impacting business services it has none of
the ONE incident type PagerDuty ships to every account. Unlike /abilities — where the 2026-09-01 refusal found the document enumerated nothing — this list's contents are stated twice in the pinned document: the 200 on this operation says "The default incident type will automatically return on this list", and `IncidentType.parent` says a type with no parent "is created under top level (`incident_default`)". So the account has exactly one type until somebody writes another, and writing is out. The two NAMES are PagerDuty's own vocabulary for its own built-in row; the document's EXAMPLE id (P123456) and its 2023 timestamps are NOT copied — the id is minted by the same pdId every other object uses and the timestamps are the org's own creation instant. NOT the documented 402: the captured ability list carries `enable_custom_fields_on_incidents` and `premium_custom_fields`, so refusing would contradict the list published one path over
the ONE incident type PagerDuty ships to every account. Unlike /abilities — where the 2026-09-01 refusal found the document enumerated nothing — this list's contents are stated twice in the pinned document: the 200 on this operation says "The default incident type will automatically return on this list", and `IncidentType.parent` says a type with no parent "is created under top level (`incident_default`)". So the account has exactly one type until somebody writes another, and writing is out. The two NAMES are PagerDuty's own vocabulary for its own built-in row; the document's EXAMPLE id (P123456) and its 2023 timestamps are NOT copied — the id is minted by the same pdId every other object uses and the timestamps are the org's own creation instant. NOT the documented 402: the captured ability list carries `enable_custom_fields_on_incidents` and `premium_custom_fields`, so refusing would contradict the list published one path over
refuse — live authentication machinery, not engineering-org data: an inventory of the OAuth tokens and browser sessions currently issued against the account is a security surface whose truthful content changes by the minute and whose simulated content would be a set of credentials that authenticate nothing. The canon records no session and no token, and a replica that invented one would be publishing credential-shaped strings
refuse — live authentication machinery, not engineering-org data: an inventory of the OAuth tokens and browser sessions currently issued against the account is a security surface whose truthful content changes by the minute and whose simulated content would be a set of credentials that authenticate nothing. The canon records no session and no token, and a replica that invented one would be publishing credential-shaped strings
refuse — network-security configuration behind an enrollment gate this replica is not enrolled in: every operation in this family requires the `X-EARLY-ACCESS: ip-allow-lists` header AND an account enrolled in the IP Allow Lists Early Access program, and its own description limits it to "Account Owners, Global Admins, and Account API Keys". An allow-list of CIDR ranges is also a claim about network perimeter that a public sandbox has no business simulating
refuse — network-security configuration behind an enrollment gate this replica is not enrolled in: every operation in this family requires the `X-EARLY-ACCESS: ip-allow-lists` header AND an account enrolled in the IP Allow Lists Early Access program, and its own description limits it to "Account Owners, Global Admins, and Account API Keys". An allow-list of CIDR ranges is also a claim about network perimeter that a public sandbox has no business simulating
refuse — network-security configuration behind an enrollment gate this replica is not enrolled in: every operation in this family requires the `X-EARLY-ACCESS: ip-allow-lists` header AND an account enrolled in the IP Allow Lists Early Access program, and its own description limits it to "Account Owners, Global Admins, and Account API Keys". An allow-list of CIDR ranges is also a claim about network perimeter that a public sandbox has no business simulating
refuse — account-administration machinery this replica does not model and should not simulate: seat licensing and plan allocation are a BILLING relationship, and this canon models an engineering organization rather than a customer of PagerDuty. There is no canonical row that could answer without inventing a plan for olympus-labs — the exact objection the 2026-09-01 /abilities refusal raised and the capture later answered for THAT row only. PagerDuty's own 4xx is the true answer here, not our coverage 404
refuse — account-administration machinery this replica does not model and should not simulate: seat licensing and plan allocation are a BILLING relationship, and this canon models an engineering organization rather than a customer of PagerDuty. There is no canonical row that could answer without inventing a plan for olympus-labs — the exact objection the 2026-09-01 /abilities refusal raised and the capture later answered for THAT row only. PagerDuty's own 4xx is the true answer here, not our coverage 404
refuse — `deprecated: true` upstream ("deprecated as OAuth token revocation is now synchronous"), limited by its own description to "account owners and admins", AND the document declares NO 200 response for it at all — the responses are 400/401/403/404/429. There is no success shape to conform to, so a 200 of any kind would be a body this replica invented outright
refuse — account session-security policy (absolute and idle session TTLs), which is administration rather than engineering data — and the document states PagerDuty's own answer for an account that has not configured one: "If no configurations exist, a 404 Not Found error will be returned. A Session Configuration needs to be created before it can be retrieved." Creating one is a write, so the 404 IS this account's true answer rather than a coverage gap
TRUE AGGREGATIONS over the incident's own timeline, not statistics invented at render time: every duration is arithmetic over `Incident.openedOffset`/`acknowledgedOffset`/`resolvedOffset`, every count is over the `Page` rows the policy actually sent, and the business/off/sleep interruption buckets are computed from each page's sent instant on the RESPONDER's own clock (`Person.utcOffsetMin`) at PagerDuty's published boundaries. Six declared fields stay ABSENT because the canon states no fact behind them — `auto_resolved`, `major`, `snoozed_seconds`, `user_defined_effort_seconds` and the two `joined_user_*` — and a flat object of optional integers is exactly where a plausible number would never be caught
one row per `Page`: who was notified, at which rung, when it was sent and when they answered. `responder_type` is read off the rung (0 IS the assignment, above it is the escalation) and `reassigned`/`added_responder` are unreachable because both are writes — a fact about a read-only universe rather than an unwritten branch
canon's `Deployment` rows in PagerDuty's own vocabulary — its `related_change_events` operation defines a change event as "service changes such as deploys, build completion, and configuration changes", which is the entity the git canon already records: a real commit, to a real environment, by a real person. `custom_details.commit` is the SAME sha every git host serves for that deploy, so a responder can follow one change across providers (invariant #5). `integration`, `routing_key`, `source`, `links` and `images` are omitted because this universe has no inbound integration — the reason the renderer already omits `Service.integrations`. `change_events` is one of the 53 names in the captured /abilities response, so the surface and the ability list agree
canon's `Deployment` rows in PagerDuty's own vocabulary — its `related_change_events` operation defines a change event as "service changes such as deploys, build completion, and configuration changes", which is the entity the git canon already records: a real commit, to a real environment, by a real person. `custom_details.commit` is the SAME sha every git host serves for that deploy, so a responder can follow one change across providers (invariant #5). `integration`, `routing_key`, `source`, `links` and `images` are omitted because this universe has no inbound integration — the reason the renderer already omits `Service.integrations`. `change_events` is one of the 53 names in the captured /abilities response, so the surface and the ability list agree
the deploy that landed nearest before the pager went off, on a repo the incident's own service ships from — labelled `most_recent`, which is the ONE of PagerDuty's three correlation reasons this canon can state as a fact. `related_service` would need a service-dependency graph the canon carries no edges for and `intelligent` names a model's output, so neither is emitted rather than being attached to the same row
canon's `Deployment` rows in PagerDuty's own vocabulary — its `related_change_events` operation defines a change event as "service changes such as deploys, build completion, and configuration changes", which is the entity the git canon already records: a real commit, to a real environment, by a real person. `custom_details.commit` is the SAME sha every git host serves for that deploy, so a responder can follow one change across providers (invariant #5). `integration`, `routing_key`, `source`, `links` and `images` are omitted because this universe has no inbound integration — the reason the renderer already omits `Service.integrations`. `change_events` is one of the 53 names in the captured /abilities response, so the surface and the ability list agree
served VERBATIM from a captured response (packages/conformance/specs/pagerduty/captures/abilities.json, sha-pinned in coverage/provider-pins.yaml under pagerduty.captures-file): 53 ability names captured 2026-09-02 from api.pagerduty.com using PagerDuty's PUBLIC developer-docs sample credentials, so the evidence is reproducible by anyone rather than taken on trust. That capture is the exact unblock the 2026-09-01 refusal named. It reaches PagerDuty's DEMO account, which is feature-rich — the operation says an ability depends on 'your pricing plan or account state', so no single list is correct and a captured one beats a chosen one
the 204/404 membership test over the same captured list — every one of the 53 answers 204 and anything else takes the document's own NotFound. NOT the 402: that response reads 'Account does not have the abilities to perform the action' and is declared on eighty-five other operations, so it is the product's generic plan gate on an ACTION rather than this operation's membership answer
hello-13's `features.vendorCatalog`: twelve FICTIONAL integration partners, structure in canon's VENDOR_CATALOG, names and directory copy in themes/greek/vendors.ts, baked into the artifact's `vendor` table at compile. This REPLACES the 2026-09-02-morning `empty` ruling, which was itself a knowing deviation — the founder's afternoon ruling took the third option over both serving nothing and printing real companies' names. A pre-catalogue artifact (both frozen g7 pins) still answers 200 + the empty envelope, deliberately, not a generation gap
one partner by id, byte-identical to the row the catalogue list served; an id this catalogue never issued is the document's own 404. The `{"vendor": [<Vendor>]}` ARRAY envelope is the pinned document's, not ours — the only single-object GET in it wearing a collection envelope, and no 200 on this provider has ever been captured to contradict it (coverage/provider-pins.yaml capture-pending)
refuse — the ONE audit operation the document gates explicitly, in its own words: "Only admins, account owners, or global API tokens on PagerDuty accounts with the 'Audit Trail' feature can access this endpoint." The account-wide trail is both admin-only and plan-gated, and no ability in the captured /abilities response names Audit Trail. The RESOURCE-scoped audit rows carry no such sentence and are classified `generate` below — the split is the document's, not ours
03 / What's simulated
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.
The incident here and the Sentry issue name the same person and the same service; the trigger log entry is at or before Sentry's `lastSeen` for the error group, and the resolve entry is after the commit that fixed it.
Against Sentry · parity/incident-parity.test.ts
The incident that paged here is the one customers wrote in about: the Salesforce Cases filed during the outage carry this incident's id, none of them was opened before it was, and each resolves to an Account and a Contact the canon names.
Against Salesforce · parity/crm-parity.test.ts
Every note on an incident is a message the Slack host serves in that incident's channel, byte for byte.
Against Slack · parity/incident-parity.test.ts
A status-page post here and the Statuspage incident for it are one publication in two dialects: the same title, the same number of updates at the same instants with the same text, and the same affected services — a post_update's impacted page-service is the same business service as the incident_update's affected component.
Against Statuspage · parity/status-page-parity.test.ts
04 / Known deviations
These answer with real data and match what PagerDuty actually returns, but fail the spec we vendor from PagerDuty — 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.
GET /incidents/{id}/log_entriesincidentsGET /incidents/custom_fieldsincident-typesGET /incidents/custom_fields/{field_id}incident-typesGET /log_entrieslog-entriesGET /log_entries/{id}log-entries05 / Pinned snapshots
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.
pd-v2.snap.sandboxapis.devgeneration 72servedpd-v2-g10.snap.sandboxapis.devgeneration 102servedpd-v2-g11.snap.sandboxapis.devgeneration 112servedpd-v2-g8.snap.sandboxapis.devgeneration 82servedpd-v2-g9.snap.sandboxapis.devgeneration 92servedNeed an endpoint that is not served yet?
Every row PagerDuty'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.