Point @slack/web-api (or curl) at https://slack.sandboxapis.dev via the client's slackApiUrl option (paths are /api/<method>, exactly like slack.com/api). Auth is a Bearer bot/user token (xoxb-…/xoxp-… — any value is accepted here). This is the org's team-messaging surface: channels, history, threads, reactions, and the workspace roster — the SAME story the git hosts and trackers tell, discussed. The #incident-bridge channel's burst brackets the real incident issue; #releases announcements cite real tags. Success and failure are BOTH HTTP 200 with an ok boolean — Slack's real error model.
Coverage badge
Deep16/17 · 94%
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.
slack.sandboxapis.devServed & verified
16 / 94%
Answering with real universe data, each response checked against Slack's published spec by the conformance suite on this build.
The full read API
17
Every read surface Slack publishes, deferred long tail included, minus the rows excluded by policy (5). Writes are out of scope: this universe is read-only.
Not served yet
1
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: Bearer xoxb-anything' "https://slack.sandboxapis.dev/api/conversations.list"Verified drop-in clients
@slack/web-api7.19.0The versions the conformance suite drives against this host on every build — pinned in coverage/client-pins.yaml, and watched weekly for upstream releases, because a client library moving without us is how a shipped integration breaks silently.
This spec is hand-authored, and that is a weaker guarantee.
Slack publishes no machine-readable API description, so the spec we conform to is written from their documentation plus live captures (retrieved 2026-08-25) and re-diffed against those pages on a schedule. Where a vendored spec makes conformance a mechanical check, this one is only as good as our reading of the docs — which is why it is stated here rather than buried in the manifest.
Spec of record: docs.slack.dev method docs (no living machine-readable spec — hand-authored shape-of-record) + live captures 2026-08-25
02 / Coverage by family
All 22 rows the coverage manifest carries for Slack, 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 Deep 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.
22 read surfaces in 16 families
generate — new canon entity Bookmark
this workspace has no CUSTOM emoji — the canon's reaction emoji are the standard set, which real Slack never lists here (DECISIONS 2026-08-25 [P34/slack] item 6)
no message in this universe carries a file — `message` has no attachment column and the fleet's only file entity, `attachment`, hangs off the support desk's ticket comments and macros and - from hello-18's attachmentParents - issues, issue comments, merge requests, alerts and the namespace, never off a chat message (clause re-worded at the hello-18 cutover 2026-09-12: the canon grew, the conclusion did not move); Slack's own answer for a workspace with no files is {ok:true, files:[], paging:{...}} at 200. RULED by the founder 2026-09-12 (round 19), coverage wave W5. It is already the third statement of one fact on this host: reactions.get refuses a `file` argument with file_not_found, and files.upload is a write
no message in this workspace is pinned, on any channel — which is what keeps conversations.history's pin_count: 0 coherent (DECISIONS 2026-08-25 [P34/slack] item 6)
SERVED (wave W5) by a BOUNDED message search over the messaging canon — the soql.ts/sosl.ts contract in a third grammar: every modifier outside the subset is refused BY NAME with the coverage headers inside Slack's ok:false envelope, never ignored, because a search that quietly drops `has:link` returns MORE matches than the caller asked for and looks exactly like a correct answer. COVERED: bare terms (all must match, on whole-word tokens), quoted phrases, `in:` by name or id, `from:` by login, id or <@U…>, count/page with the doc's own 100 ceilings, sort=timestamp and sort_dir. REFUSED BY NAME: every other modifier, `-negation`, explicit AND/OR, wildcards, `highlight=true` and cursormark pagination. `sort=score` is NOT refused even though this replica computes no relevance — it is the default, refusing it would refuse every naive call, and the SET and the counts are exact while Slack's match objects carry no score field for us to invent a value for; the order served is the documented newest-first. One narrowing is stated rather than hidden: this replica matches message TEXT only, where the doc says users and channels also match at a lower priority — narrower than the real API, never wider. Matches come from BOTH halves of a channel's history, top-level and thread replies, because this canon puts the detail in the threads
03 / What's simulated
Every Slack call resolves against the same simulated data set every other provider serves. Counted from the slack-2026-08-g11 artifact (universe generation 11):
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.
Every message body, every whole-second timestamp, the whole top-level/reply partition, the per-message reaction sets and the user directory are the same on this host and on Microsoft Graph — each dialect's own documented ordering normalised away first, and the residue asserted identical over full cursor walks, not samples. The channel roster served here is the canonical roster exactly; the Graph team holding that channel contains every one of them.
Against Microsoft Teams · parity/messaging-parity.test.ts
Every note PagerDuty prints on an incident is a message this host serves in that incident's channel, byte for byte.
Against PagerDuty · parity/incident-parity.test.ts
Hop 1 of the four-hop walk: the incidents channel, its thread opener and every reply are served here, and the burst's timestamps ascend and fall between the Jira issue's creation and its resolution — the same window the GitHub merge lands in.
Against Jira, Linear, GitHub, GitLab · parity/pinned-cross-category.test.ts
05 / 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.
slack-2026-08.snap.sandboxapis.devgeneration 6unversionedservedslack-2026-08-g10.snap.sandboxapis.devgeneration 10unversionedservedslack-2026-08-g11.snap.sandboxapis.devgeneration 11unversionedservedslack-2026-08-g8.snap.sandboxapis.devgeneration 8unversionedservedslack-2026-08-g9.snap.sandboxapis.devgeneration 9unversionedservedNeed an endpoint that is not served yet?
Every row Slack'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.