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

Salesforce

Point a Salesforce client at https://sf.sandboxapis.dev as its INSTANCE URL — jsforce takes instanceUrl on the Connection constructor, simple-salesforce takes instance_url, the sf CLI takes --instance-url. Paths mirror an org exactly: /services/data for the version list, /services/data/v67.0/... for everything else. Any access token, or none, is accepted. WHAT IS SERVED, exactly four operations: GET /services/data (37 versions, a verbatim capture of a real org's response); GET /services/data/v67.0/sobjects/{obj}/describe; GET /services/data/v67.0/sobjects/{obj}/{id}; and GET /services/data/v67.0/query?q=<SOQL> — over FOUR objects only: Account, Contact, Opportunity, Case. THE SOQL IS A BOUNDED SUBSET: SELECT (and COUNT()), FROM, WHERE with AND/OR/NOT/parens/IN/LIKE, ORDER BY with NULLS placement, LIMIT and OFFSET. Anything else — a relationship subquery, a parent field path like Account.Name, GROUP BY, SUM(), FIELDS(ALL), WITH SECURITY_ENFORCED, a week-based date literal — answers 400 NAMING the construct, never a wrong answer and never a silently-ignored clause. Everything outside those four operations (describeGlobal, /queryAll, list views, search, limits, analytics, bulk) answers Salesforce's own 404 with an x-sandboxapis-coverage header, meaning NOT BUILT YET rather than broken — deliberately not an empty result set, which would be a lie you could not detect. ONE THING TO KNOW ABOUT PINS: the CRM ships with generation hello-14, and every generation after it inherits it. Live rides hello-19. The newest pin, sf-v67-g11, rides hello-16; sf-v67-g10 rides hello-15 and sf-v67-g9 rides hello-14. All four operations answer on all four of those. Only sf-v67-g8 is older — it rides hello-13, which predates the CRM, so there the three CRM operations answer a 404 that says so in words while /services/data keeps working. Check https://sandboxapis.dev/providers/salesforce for what has landed since.

REST · 50 endpointsMCP-ready

Coverage badge

Partial23/50 · 46%

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
sf.sandboxapis.dev
Pinned hosts
4 — generation 8, 9, 10, 11
Serving since
2026-09-04
Lifecycle
Live

Served & verified

21 / 42%

Answering with real universe data, each response checked against Salesforce's published spec by the conformance suite on this build.

The full read API

50

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

Not served yet

29

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://sf.sandboxapis.dev/services/data/v67.0/query?q=SELECT+Id,Name+FROM+Account+LIMIT+5"

Verified drop-in clients

No client library is version-pinned against Salesforce yet, so this page claims none. What is asserted is the wire: every response above is validated against Salesforce'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.

This spec is hand-authored, and that is a weaker guarantee.

Salesforce publishes no machine-readable API description, so the spec we conform to is written from their documentation plus live captures (retrieved 2026-09-04) 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: hand-authored from Salesforce's documented core REST resource list + the live captures below

02 / Coverage by family

Every read surface, grouped the way Salesforce groups it.

All 50 rows the coverage manifest carries for Salesforce, 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 Partial 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.

50 read surfaces in 15 families

sobject-describe4 of 7 served & verified
GET
/services/data/{version}/sobjects

SERVED (wave W5). describeGlobal's `sobjects` array is the SUMMARY half of the describe this host already serves, and the wave made that literally true rather than nearly true: `objectDescribe()` in packages/renderer-salesforce/src/describe.ts is the full describe MINUS `fields` and `childRelationships`, computed by deleting those two keys rather than by writing a second literal, which is Salesforce's own relationship between its two describe types. So this host cannot report `queryable: true` on one surface and `queryable: false` on the other for the same object, and conformance asserts the global summary against the describe ROUTE as well as against the renderer. `encoding` and `maxBatchSize` are facts about the API rather than about the org. The operation's evidence was `path-only`, so this wave declared its shape under the `hand-authored` class the describe row established. Listing exactly the four served objects is a true statement about THIS replica — the same statement the describe's coverage 404 makes about a fifth

served & verified
Core
GET
/services/data/{version}/sobjects/{sObject}

SERVED (wave W5): `{objectDescribe, recentItems}`, where `objectDescribe` is the same summary describeGlobal lists. The one shape ruling this row owed is made and is not a stub — `recentItems` is EMPTY, because a most-recently-used list is per-USER interface state written by the Salesforce web app and this universe carries no Salesforce session, which is the same fact the describe beside it publishes as `mruEnabled: false`. Conformance asserts BOTH halves on one response, so the flag and the list cannot come to say different things. The row stays `derive` rather than `empty`: `objectDescribe` carries real content, and `empty` names a row whose WHOLE answer is nothing — that row is `/recent`, ruled on this same fact

served & verified
Core
GET
/services/data/{version}/sobjects/{sObject}/describe

SERVED for Account, Contact, Opportunity and Case — the founder-scoped core. The field list is not assembled from recollection: it is GENERATED from the same served-field table the record projection and the SOQL name resolver read, so describe cannot advertise a field a record does not carry or omit one a query would accept. Picklist values are derived from the canon enums' projection tables, so the metadata cannot offer a value no row could hold; child relationships are declared only where the child really carries the foreign key. Every write flag is false because that is TRUE of a read-only replica, which is what lets a client discover the boundary without hitting it. An sObject outside the four answers the coverage 404 — a real org has Leads and we have not built them, and the 404 says which of those two it means

served & verified
Core
GET
/services/data/{version}/sobjects/{sObject}/describe/approvalLayouts

no approval process is defined on any served object, so no object has an approval layout - the same fact /process/approvals states; Salesforce's own answer for an object with none is {"approvalLayouts": []} at 200. RULED by the founder 2026-09-12 (round 19), coverage wave W5. This row is different IN KIND from its two siblings, which is why it could be ruled while they stay `generate`: `compactLayouts` and `layouts` need a CompactLayout and a PageLayout entity nobody has built, so their emptiness would be a claim about what this replica has not modelled, whereas an approval layout exists PER APPROVAL PROCESS and there is no approval process for one to belong to

served & verified
Core
GET
/services/data/{version}/sobjects/{sObject}/describe/compactLayouts

generate — a compact layout is ORG UI CONFIGURATION — the field set a record's highlights panel shows, per record type — and canon models no layout of any kind; the describe this host serves reports `compactLayoutable: false` for exactly that reason. New canon entity CompactLayout. Nothing about a generation blocks it: the four sObjects are described today

planned
Core
GET
/services/data/{version}/sobjects/{sObject}/describe/layouts

generate — a page layout is ORG UI CONFIGURATION — field sections, related lists, buttons, and the record-type and profile assignments that decide which layout a user gets — and canon models none of it; the describe this host serves reports `layoutable: false` for exactly that reason. New canon entities PageLayout and PageLayoutAssignment. Nothing about a generation blocks it: the four sObjects are described today

planned
Core
GET
/services/data/{version}/sobjects/{sObject}/quickActions

generate — a quick action is ORG UI CONFIGURATION — the create, update and log-a-call actions an admin defines on an object — and canon models none of them. New canon entity QuickAction. READ-ONLY IS NOT THE BLOCKER: reading an action's DEFINITION is not performing it, and this host would report one the way the describe already reports `createable: false`. Nothing about a generation blocks it: the four sObjects are described today

planned
Core
analytics-reports0 of 6 served & verified
GET
/services/data/{version}/analytics/dashboards

generate — the Reports and Dashboards API is a separate product surface over the same records: it needs the CRM canon first, and then report definitions the canon does not model

deferred
Long tail
GET
/services/data/{version}/analytics/dashboards/{dashboardId}

generate — the Reports and Dashboards API is a separate product surface over the same records: it needs the CRM canon first, and then report definitions the canon does not model

deferred
Long tail
GET
/services/data/{version}/analytics/reports

generate — the Reports and Dashboards API is a separate product surface over the same records: it needs the CRM canon first, and then report definitions the canon does not model

deferred
Long tail
GET
/services/data/{version}/analytics/reports/{reportId}

generate — the Reports and Dashboards API is a separate product surface over the same records: it needs the CRM canon first, and then report definitions the canon does not model

deferred
Long tail
GET
/services/data/{version}/analytics/reports/{reportId}/describe

generate — the Reports and Dashboards API is a separate product surface over the same records: it needs the CRM canon first, and then report definitions the canon does not model

deferred
Long tail
GET
/services/data/{version}/analytics/reports/{reportId}/instances

generate — the Reports and Dashboards API is a separate product surface over the same records: it needs the CRM canon first, and then report definitions the canon does not model

deferred
Long tail
bulk0 of 5 served & verified
GET
/services/data/{version}/jobs/ingest

generate — Bulk 2.0 job state is a record of asynchronous work; a read-only universe has no jobs until the canon generates a history of them

deferred
Long tail
GET
/services/data/{version}/jobs/ingest/{jobId}

generate — Bulk 2.0 job state is a record of asynchronous work; a read-only universe has no jobs until the canon generates a history of them

deferred
Long tail
GET
/services/data/{version}/jobs/query

generate — Bulk 2.0 job state is a record of asynchronous work; a read-only universe has no jobs until the canon generates a history of them

deferred
Long tail
GET
/services/data/{version}/jobs/query/{jobId}

generate — Bulk 2.0 job state is a record of asynchronous work; a read-only universe has no jobs until the canon generates a history of them

deferred
Long tail
GET
/services/data/{version}/jobs/query/{jobId}/results

generate — Bulk 2.0 job state is a record of asynchronous work; a read-only universe has no jobs until the canon generates a history of them

deferred
Long tail
list-views0 of 4 served & verified
GET
/services/data/{version}/sobjects/{sObject}/listviews

generate — a list view is org configuration plus the records it selects — both are CRM canon (hello-14)

deferred
Next
GET
/services/data/{version}/sobjects/{sObject}/listviews/{listViewId}

generate — a list view is org configuration plus the records it selects — both are CRM canon (hello-14)

deferred
Next
GET
/services/data/{version}/sobjects/{sObject}/listviews/{listViewId}/describe

generate — a list view is org configuration plus the records it selects — both are CRM canon (hello-14)

deferred
Next
GET
/services/data/{version}/sobjects/{sObject}/listviews/{listViewId}/results

generate — a list view is org configuration plus the records it selects — both are CRM canon (hello-14)

deferred
Next
soql-query2 of 4 served & verified
GET
/services/data/{version}/query

SERVED. Both blockers named on this row are cleared: hello-14 supplies the records, and the serving wave supplied the BOUNDED SOQL PARSER — SELECT (incl. COUNT()), FROM over the four serve-plan objects, WHERE with AND/OR/NOT/parens/IN/LIKE, ORDER BY with NULLS placement, LIMIT and OFFSET. Every construct outside that subset is refused BY NAME with a 400 and the query coverage headers, never approximated and never silently dropped — a WHERE clause the parser did not understand and skipped would return more rows than the caller asked for and look exactly like a correct answer, which the caller cannot detect. Relative date literals resolve against the artifact ANCHOR, not the wall clock, so a pinned snapshot answers the same query forever. renderer-jira's bounded JQL is the precedent, including its two-tier error: a coverage refusal carries the pointer, a caller's syntax error does not

served & verified
Core
GET
/services/data/{version}/queryAll

SERVED (wave W5), and this row OVERTURNS ITS OWN EARLIER REASON — flagged as a founder call in the wave's PR. The 2026-09-08 reading was that queryAll returning exactly what query returns is `a fact about THIS universe, not about the operation`. The correction is that queryAll is SPECIFIED as `query, plus the deleted and archived rows`, and running that specification against an organization that has deleted and archived nothing produces query's output by the operation's own definition — the same way `WHERE IsDeleted = true` correctly returns nothing here. Refusing it was refusing to compute a specified answer because the answer looked too easy. The client-reliance worry is answered rather than dismissed: nothing on the wire tells a caller the two are identical, and the conformance entry asserts IsDeleted is false on every record BEFORE it compares the two, so the day canon deletes something the row goes red rather than going on serving a claim that has quietly become false. If the earlier reading is preferred the route comes out and this row returns to `generate` `record deletion and archival history`, with nothing else in the wave touched

served & verified
Core
GET
/services/data/{version}/query/{queryLocator}

refuse — this host never issues a query locator - one is minted only when a result exceeds QUERY_BATCH_SIZE (2000, renderer-salesforce/src/soql.ts) and the largest served object has 120 records - so the route can only be reached with a locator it did not issue, and Salesforce's own INVALID_QUERY_LOCATOR is the whole answer (the proposal already stands in coverage/reviews/2026-09-08-modes-generation-claims.md). RULED by the founder 2026-09-12 (round 19), coverage wave W5. 400, because this is a malformed continuation rather than a missing resource: the route exists and was reached, and it was the ARGUMENT that could not be honoured

planned
Core
GET
/services/data/{version}/queryAll/{queryLocator}

refuse — this host never issues a query locator - one is minted only when a result exceeds QUERY_BATCH_SIZE (2000, renderer-salesforce/src/soql.ts) and the largest served object has 120 records - so the route can only be reached with a locator it did not issue, and Salesforce's own INVALID_QUERY_LOCATOR is the whole answer (the proposal already stands in coverage/reviews/2026-09-08-modes-generation-claims.md). RULED by the founder 2026-09-12 (round 19), coverage wave W5. The other blocker this row used to name is gone rather than waived: /queryAll is served now, so the only thing standing here is the locator

planned
Core
sosl-search2 of 4 served & verified
GET
/services/data/{version}/parameterizedSearch

SERVED (wave W5), by the SAME bounded SOSL engine `/search` runs — one grammar, two spellings, so `what does the word acme match` has exactly one answer on this host. Both blockers this row named are cleared: the CRM canon landed with hello-14 and the query grammar with the SOQL parser on 2026-09-04, and what was left was renderer work of exactly the kind soql.ts already did. `q` goes through the same term parser that reads the inside of a `FIND {}` clause; `sobject`, `fields`, `in` and `overallLimit` are covered; and an unrecognized parameter is refused BY NAME rather than ignored, because `spellCorrection`, `highlight`, `netId` and the per-object `where` family each NARROW or ANNOTATE a result and honouring the request minus one returns a wider answer than the caller asked for

served & verified
Next
GET
/services/data/{version}/search

SERVED (wave W5) by a BOUNDED SOSL parser — the soql.ts pattern applied a second time, with the same contract: every construct outside the subset is refused BY NAME with a 400 and the query coverage headers, never approximated and never silently dropped. Both blockers this row named are cleared: the CRM canon landed with hello-14 and the query grammar with the SOQL parser on 2026-09-04. COVERED: `FIND {term}`/`FIND 'term'` over one word (optionally ending in `*`) or one quoted phrase, `IN ALL|NAME|EMAIL|PHONE FIELDS`, `RETURNING Object(fields)` and `LIMIT`. REFUSED BY NAME: a multi-word term — because Salesforce's default operator between bare terms is not something this repository can check (developer.salesforce.com answers 403 to every machine fetch) and the two candidate readings return DIFFERENT result sets — plus AND/OR/NOT, interior wildcards, `IN SIDEBAR FIELDS` (org UI configuration canon does not model) and every sub-clause of a RETURNING. One narrowing is stated rather than hidden: this replica matches record TEXT only, where Salesforce also matches adjacent objects at a lower priority, which makes the result set narrower than the real API's and never wider

served & verified
Next
GET
/services/data/{version}/search/layout

generate — SOSL searches across objects, so it needs the CRM canon AND the query grammar SOQL needs; it follows /query rather than leading it

deferred
Next
GET
/services/data/{version}/search/scopeOrder

generate — SOSL searches across objects, so it needs the CRM canon AND the query grammar SOQL needs; it follows /query rather than leading it

deferred
Next
ui-metadata1 of 4 served & verified
GET
/services/data/{version}/recent

no user has viewed a record in this replica - the recently-viewed list is per-user UI state written by the Salesforce web app, this universe carries no Salesforce session, and the served describe reports mruEnabled false for exactly that reason; Salesforce's own answer for a user who has viewed nothing is [] at 200. RULED by the founder 2026-09-12 (round 19), coverage wave W5. This is the row that gives `mruEnabled: false` a resource behind it: the conformance entry asserts both halves, so the flag and the list cannot come to say different things

served & verified
Long tail
GET
/services/data/{version}/appMenu/{appMenuType}

generate — tabs, theme, recently-viewed and the app menu are per-org and per-USER state; nothing in this universe carries a Salesforce user's recent-item list yet

deferred
Long tail
GET
/services/data/{version}/tabs

generate — tabs, theme, recently-viewed and the app menu are per-org and per-USER state; nothing in this universe carries a Salesforce user's recent-item list yet

deferred
Long tail
GET
/services/data/{version}/theme

generate — tabs, theme, recently-viewed and the app menu are per-org and per-USER state; nothing in this universe carries a Salesforce user's recent-item list yet

deferred
Long tail
actions1 of 3 served & verified
GET
/services/data/{version}/actions/custom

olympus-labs' Salesforce org defines no custom invocable action - canon models no Flow, no Apex class and no automation on the CRM side, and this replica accepts no writes; Salesforce's own answer for an org with none is an empty actions collection at 200. RULED by the founder 2026-09-12 (round 19), coverage wave W5

served & verified
Long tail
GET
/services/data/{version}/actions

generate — the invocable-action catalogue reflects an org's automation, which the CRM canon does not model yet and no capture reaches

deferred
Long tail
GET
/services/data/{version}/actions/standard

generate — the invocable-action catalogue reflects an org's automation, which the CRM canon does not model yet and no capture reaches

deferred
Long tail
process3 of 3 served & verified
GET
/services/data/{version}/process/approvals

olympus-labs' Salesforce org defines no approval process - canon models no approval automation on the CRM side (the one approval_rule row is a code-review rule over repo-01) and this replica accepts no writes; Salesforce's own answer for an org with none is an empty approvals collection at 200. RULED by the founder 2026-09-12 (round 19), coverage wave W5. The collection is a MAP keyed by sObject name rather than an array, which is Salesforce's own shape, so the empty one is {}

served & verified
Long tail
GET
/services/data/{version}/process/rules

olympus-labs' Salesforce org defines no workflow rule - canon models no CRM automation and this replica accepts no writes; Salesforce's own answer for an org with none is {"rules": []} at 200. RULED by the founder 2026-09-12 (round 19), coverage wave W5

served & verified
Long tail
GET
/services/data/{version}/process/rules/{sObject}

olympus-labs' Salesforce org defines no workflow rule - canon models no CRM automation and this replica accepts no writes; Salesforce's own answer for an org with none is {"rules": []} at 200. RULED by the founder 2026-09-12 (round 19), coverage wave W5. Asserted on all four served objects, because `no workflow rule anywhere` is a claim about every one of them; an object this replica does not serve still earns the coverage 404, so the ruling never answers a typo confidently

served & verified
Long tail
sobject-rows3 of 3 served & verified
GET
/services/data/{version}/sobjects/{sObject}/{fieldName}/{fieldValue}

SERVED (wave W5): retrieve by EXTERNAL ID over the `idLookup` fields the describe already advertises — every Id, plus Contact.Email and Case.CaseNumber. Conformance asserts the record it answers is BYTE-IDENTICAL to the one the by-id retrieve answers, and walks every field the describe marks idLookup so a flag this host publishes cannot advertise a lookup that does not work. The ruling this row owed is made: a lookup on a field whose `idLookup` is FALSE is refused BY NAME with a 400 and `INVALID_FIELD`, naming the field and listing the ones that would work — and with NO coverage pointer, because the real API refuses it too and pointing at our manifest would misdirect somebody who needs to look at their own request. A field this replica does not serve at all is the other refusal and does carry the pointer, because the served field list is a subset of the real object's. A value that is not unique on its field answers Salesforce's own 300 with the matching record paths, rather than the first match

served & verified
Core
GET
/services/data/{version}/sobjects/{sObject}/{id}

SERVED. Every field is read out of the artifact's CRM tables; the 18-character Id is derived from the CANONICAL id (never from a row's position, so an id does not move when a company is inserted ahead of it) with Salesforce's own published case-safe checksum, checked in ids.test.ts against Salesforce's published 15->18 pairs. Both id lengths are accepted the way the real API accepts them and the 18-character form is always returned. A field with no canon value serves an explicit null rather than being omitted — the opposite of this fleet's usual rule, because Salesforce does the opposite and `record.Phone` is a property access in every client ever written. A well-formed id that names nothing is a DATA 404 with NO coverage pointer, which is the other 404 and a different statement

served & verified
Core
GET
/services/data/{version}/sobjects/{sObject}/{id}/{relationshipFieldName}

SERVED (wave W5): a parent traversal reads the PARENT RECORD, and every foreign key it walks was already served — Contact.AccountId, Case.AccountId, Case.ContactId and Contact.ReportsToId all name objects this host describes and projects. Conformance walks EVERY non-null reference on all four objects and asserts the traversal answers byte-identically to fetching that parent by Id, which is invariant #5 at the one route whose whole job is to follow a reference. Three branches are decisions rather than plumbing: a relationship name the object does not have is a plain 400 naming the ones it does (callers most often write the FIELD name, `AccountId`, where the API wants the RELATIONSHIP name, `Account`); `OwnerId` references `User`, which is not among the four founder-scoped objects, so `Owner` answers the coverage 404 until a User sObject ships; and a reference that is NULL answers 204 No Content, because the record was found and the relationship is empty, which is a different statement from `no such record`. THE PATH SHAPE IS SHARED with the external-id retrieve and is disambiguated the way the real API does it — whether the middle segment is a well-formed Salesforce id whose key prefix is this object's

served & verified
Core
change-tracking2 of 2 served & verified
GET
/services/data/{version}/sobjects/{sObject}/deleted

nothing in this universe is ever deleted - the generator writes an append-only event log, this replica accepts no writes, and IsDeleted is false on every record the describe publishes (renderer-salesforce/src/sobjects.ts:472); Salesforce's own answer for a window with no deletions is {"deletedRecords": [], "earliestDateAvailable": ..., "latestDateCovered": ...} at 200. RULED by the founder 2026-09-12 (round 19), coverage wave W5. `earliestDateAvailable` is the instant this snapshot's history of the object begins - the earliest CreatedDate among its records - because this replica's horizon is the artifact's own history rather than the rolling 30-day retention real Salesforce reports there; reporting a retention window this host does not implement would be the invention

served & verified
Next
GET
/services/data/{version}/sobjects/{sObject}/updated

SERVED (wave W5). The blocker this row named — `computed over record history, which the CRM canon has to carry` — was cleared by hello-14: every served table carries `updated_epoch`, which is the SAME column `auditFields` in sobjects.ts publishes as LastModifiedDate and SystemModstamp, so the window is a filter over a field this host already serves rather than a history it has to invent. Conformance asserts the window against the equivalent `WHERE LastModifiedDate >= …` SOQL query, so if a later change points one of them at CreatedDate the two stop agreeing. `start` and `end` must carry an EXPLICIT UTC offset, which is a determinism rule rather than pedantry: `Date.parse` resolves an offsetless dateTime against the runtime's local zone, so the same window would select different records on different machines and `pnpm determinism` — which byte-compares artifacts, not renderer output — could not catch it. Salesforce's 30-day retention horizon is deliberately NOT enforced: this replica has no rolling retention, and `/deleted` reports the horizon it does have instead

served & verified
Next
limits1 of 2 served & verified
GET
/services/data/{version}/limits/recordCount

SERVED (wave W5). The blocker this row named — `serving numbers before the CRM canon exists would mean inventing them` — was cleared by hello-14, which ships the CRM canon and rides every artifact from `sf-v67-g9` forward. recordCount is `{sObjects: [{count, name}]}` over exactly the four objects this host describes, every count is a row count in the artifact, and conformance asserts each against a `SELECT COUNT()` on the same object so the two surfaces cannot disagree. `/limits` ITSELF STAYS UNSERVED and that is the honest half of this row: an org's API request allowances, storage figures and license counts are facts this universe does not carry, and recordCount is the one part of that resource that is nothing but a count of rows already served. An object this replica does not serve earns the coverage refusal rather than a silent omission — a missing row would report nothing for an object that has plenty

served & verified
Next
GET
/services/data/{version}/limits

generate — an org's limits and per-sObject record counts are facts about THIS simulated org, not about Salesforce. Serving numbers before the CRM canon exists would mean inventing them

deferred
Next
composite1 of 1 served & verified
GET
/services/data/{version}/composite/sobjects/{sObject}

SERVED (wave W5). The reason this row carried until 2026-09-12 — `composite batches the operations above; it can be no more real than they are` — was true and is now an argument FOR serving it: the operation it batches IS served, so the batch is exactly as real as the retrieve. It reads up to 800 records of one type by Id, every one of them a record the by-id projection already produces, and conformance asserts the batch equals the one-by-one reads element for element. An Id that names nothing is a `null` IN POSITION, which is Salesforce's own answer and the reason the response is positional: dropping a miss would shift every record after it onto the wrong id. `fields` is required on the GET form, the 800-record limit is enforced rather than quietly honoured, and both refusals carry the operation's own `INVALID_INPUT`

served & verified
Next
resources0 of 1 served & verified
GET
/services/data/{version}

derive — NOT canon-blocked, and the distinction is the point of classifying it separately: the resource map is a list of the resources THIS replica routes, so it is buildable the day somebody writes it rather than the day a pin rides the CRM canon. It is unserved for one reason only — no capture reaches it (401 before routing, recorded under salesforce.capture-pending) and the hand-authored document therefore declares no shape for it, so serving one would mean publishing a body nobody has seen

planned
Core
versions1 of 1 served & verified
GET
/services/data

SERVED, and served VERBATIM from a captured response (packages/conformance/specs/salesforce/captures/versions.json, sha-pinned in coverage/provider-pins.yaml under salesforce.captures-file) — the pagerduty /abilities precedent. This is the ONLY Salesforce response anybody can observe without an org: every path under /services/data/v{version}/ answers 401 to an anonymous caller because auth runs before routing. Thirty-seven entries, v31.0 to v67.0, including the one label upstream spells with U+2019 rather than an apostrophe; salesforce/versions.test.ts binds the renderer's constant to the capture and to its sha so a well-meaning ASCII cleanup cannot turn the capture into a construction

served & verified
Core

03 / What's simulated

One data set, rendered in Salesforce'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.

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 generationSalesforce API versionRepository files
sf-v67-g10sf-v67-g10.snap.sandboxapis.devgeneration 1067.0served
sf-v67-g11sf-v67-g11.snap.sandboxapis.devgeneration 1167.0served
sf-v67-g8sf-v67-g8.snap.sandboxapis.devgeneration 867.0served
sf-v67-g9sf-v67-g9.snap.sandboxapis.devgeneration 967.0served

Need an endpoint that is not served yet?

Every row Salesforce'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