Point your Devin v3 API calls at https://devin.sandboxapis.dev (curl or a client generated from the official v3-openapi.yaml). Auth is a Bearer token (Devin's cog_ service-user keys — any value is accepted here). The enterprise scope serves the sandbox's tier-1 slice: sessions, members/users, ACU consumption (PST day buckets, as documented), and usage metrics. Session PRs link to real pulls on the git hosts, with matching identities.
Coverage badge
Deep113/122 · 93%
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.
devin.sandboxapis.devServed & verified
63 / 52%
Answering with real universe data, each response checked against Devin's published spec by the conformance suite on this build.
The full read API
122
Every read surface Devin publishes, deferred long tail included, minus the rows excluded by policy. Writes are out of scope: this universe is read-only.
Not served yet
59
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 any-key' "https://devin.sandboxapis.dev/v3/enterprise/sessions?first=5"Verified drop-in clients
No client library is version-pinned against Devin yet, so this page claims none. What is asserted is the wire: every response above is validated against Devin'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 122 rows the coverage manifest carries for Devin, 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.
122 read surfaces in 28 families
CORRECTED 2026-09-12 (hello-18 serving wave W-c): the reason this row carried said canon carries no attachment entity AT ALL, and hello-15 made that FALSE - canon.Attachment landed with the support-desk domain (packages/canon/src/support-desk.ts:962), the artifact holds 35 attachment rows with real bodies, and renderer-zendesk resolves them by id today. The empty answer still stands on the NARROWER fact, which is this row's own: every attachment in this universe hangs off a record some OTHER product owns, and NO generation attaches one to an AGENT SESSION - a session here starts from repository work rather than from a file somebody dragged in, so nobody ever uploaded one, and Devin's own answer for a session with none is the empty list this renderer's sessionAttachments() has returned since the host opened. THE ASK is an attachment whose PARENT is an agent session
`tags` has been [] on every SessionResponse this renderer serves — this org never adopted session tagging, and the sub-resource returns the same empty list the session object reports (DECISIONS 2026-08-20 [devin])
the org-scoped twin of the enterprise attachments row, held to the same answer by test. CORRECTED 2026-09-12 (hello-18 serving wave W-c): the reason this row carried said canon carries no attachment entity AT ALL, and hello-15 made that FALSE - canon.Attachment landed with the support-desk domain (packages/canon/src/support-desk.ts:962), the artifact holds 35 attachment rows with real bodies, and renderer-zendesk resolves them by id today. The empty answer still stands on the NARROWER fact, which is this row's own: every attachment in this universe hangs off a record some OTHER product owns, and NO generation attaches one to an AGENT SESSION - a session here starts from repository work rather than from a file somebody dragged in, so nobody ever uploaded one, and Devin's own answer for a session with none is the empty list this renderer's sessionAttachments() has returned since the host opened. THE ASK is an attachment whose PARENT is an agent session
the org-scoped twin of the enterprise tags row, held to the same answer by test: this org never adopted session tagging (DECISIONS 2026-08-20 [devin])
generate — new canon entity AgentSessionInsight
refuse — conversational content: the session transcript is what Devin and the engineer said to each other, and this universe does not invent it (the standing founder-adopted default; revisit if a client asks)
generate — new canon entity AgentSessionInsight
generate — new canon entity AgentSessionInsight
refuse — conversational content: the session transcript is what Devin and the engineer said to each other, and this universe does not invent it (the standing founder-adopted default; revisit if a client asks)
generate — new canon entity AgentSessionInsight
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — a snapshot blueprint and its builds exist only because a write created them, and this universe accepts no writes (invariant #3) — the async-artifact shape ruled in coverage/reviews/2026-09-01-jira-modes.md
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — Devin's code-scanning add-on is not enabled for this enterprise, and Devin answers 403 to every code-scan read for an enterprise without it — no scan, finding, metric or profile can exist here
refuse — the id half of the service-user pair: this universe's only non-human identity is the CI bot and it holds no Devin seat, so the service-user list is empty, so no id can resolve and Devin's own 404 is the only possible answer (the by-id shape ruled in DECISIONS 2026-09-01 [coverage/A0])
refuse — the id half of the service-user pair: this universe's only non-human identity is the CI bot and it holds no Devin seat, so the service-user list is empty, so no id can resolve and Devin's own 404 is the only possible answer (the by-id shape ruled in DECISIONS 2026-09-01 [coverage/A0])
CORRECTED 2026-09-12 (hello-18 serving wave W-c). The 2026-09-10 correction already replaced this row's original reason - "this universe has no identity provider" - because hello-15's canon.IdentityProviderConfig and canon.IdentityGroup (packages/canon/src/ci-config.ts:359, :401) made it false: a SAML provider with SCIM enabled, the administrator who switched it on, the date it was switched on, and four groups derived from ACCESS facts, which renderer-circleci serves today (/organizations/{org_id}/groups, ruled 2026-09-07). Two of the three supporting clauses that correction left have since gone false in their turn, and this is the second correction this row has needed for the same reason: a ruling resting on an absence canon later filled. canon DOES attach a role to an identity group (hello-17's grants - a membership row with scope_kind 'group', reader.groupRole(), which renderer-circleci serves), and canon DOES bind a person to an external uid (hello-18's externalUid - person.external_uid, which GitLab's saml/{uid} and scim/{uid} resolve). The empty answer still stands, and it now stands on the FIRST clause alone - which is the one the vendored document actually asks about: `IdpGroupUser` is "a user whose membership is derived from IDP group assignments", and NO Devin membership here is derived that way. Every Devin seat is assigned DIRECTLY to a person by the organization, which is the same absence every member detail already reports as idp_role_assignments: []. THE ASK is now one fact rather than two: a Devin SEAT a person holds BECAUSE an identity group confers it. A directory identity on a person is not that, and a role on a group is not that either. NOTE for the next wave: the five idp-GROUPS rows in this fence still carry the falsified "canon attaches no role to an identity group" clause, and they are deliberately untouched here because on those rows the correction may be a re-SERVE rather than a re-word - `IdpGroup.role_assignments` is RoleAssignment[] and hello-17's groupRole() may now answer it - which is a ruling this wave cannot make alone
the org-scoped idp-users twin, held to the same answer by test. CORRECTED 2026-09-12 (hello-18 serving wave W-c). The 2026-09-10 correction already replaced this row's original reason - "this universe has no identity provider" - because hello-15's canon.IdentityProviderConfig and canon.IdentityGroup (packages/canon/src/ci-config.ts:359, :401) made it false: a SAML provider with SCIM enabled, the administrator who switched it on, the date it was switched on, and four groups derived from ACCESS facts, which renderer-circleci serves today (/organizations/{org_id}/groups, ruled 2026-09-07). Two of the three supporting clauses that correction left have since gone false in their turn, and this is the second correction this row has needed for the same reason: a ruling resting on an absence canon later filled. canon DOES attach a role to an identity group (hello-17's grants - a membership row with scope_kind 'group', reader.groupRole(), which renderer-circleci serves), and canon DOES bind a person to an external uid (hello-18's externalUid - person.external_uid, which GitLab's saml/{uid} and scim/{uid} resolve). The empty answer still stands, and it now stands on the FIRST clause alone - which is the one the vendored document actually asks about: `IdpGroupUser` is "a user whose membership is derived from IDP group assignments", and NO Devin membership here is derived that way. Every Devin seat is assigned DIRECTLY to a person by the organization, which is the same absence every member detail already reports as idp_role_assignments: []. THE ASK is now one fact rather than two: a Devin SEAT a person holds BECAUSE an identity group confers it. A directory identity on a person is not that, and a role on a group is not that either. NOTE for the next wave: the five idp-GROUPS rows in this fence still carry the falsified "canon attaches no role to an identity group" clause, and they are deliberately untouched here because on those rows the correction may be a re-SERVE rather than a re-word - `IdpGroup.role_assignments` is RoleAssignment[] and hello-17's groupRole() may now answer it - which is a ruling this wave cannot make alone
the v3beta1 idp-users twin, held to the same answer by test. CORRECTED 2026-09-12 (hello-18 serving wave W-c). The 2026-09-10 correction already replaced this row's original reason - "this universe has no identity provider" - because hello-15's canon.IdentityProviderConfig and canon.IdentityGroup (packages/canon/src/ci-config.ts:359, :401) made it false: a SAML provider with SCIM enabled, the administrator who switched it on, the date it was switched on, and four groups derived from ACCESS facts, which renderer-circleci serves today (/organizations/{org_id}/groups, ruled 2026-09-07). Two of the three supporting clauses that correction left have since gone false in their turn, and this is the second correction this row has needed for the same reason: a ruling resting on an absence canon later filled. canon DOES attach a role to an identity group (hello-17's grants - a membership row with scope_kind 'group', reader.groupRole(), which renderer-circleci serves), and canon DOES bind a person to an external uid (hello-18's externalUid - person.external_uid, which GitLab's saml/{uid} and scim/{uid} resolve). The empty answer still stands, and it now stands on the FIRST clause alone - which is the one the vendored document actually asks about: `IdpGroupUser` is "a user whose membership is derived from IDP group assignments", and NO Devin membership here is derived that way. Every Devin seat is assigned DIRECTLY to a person by the organization, which is the same absence every member detail already reports as idp_role_assignments: []. THE ASK is now one fact rather than two: a Devin SEAT a person holds BECAUSE an identity group confers it. A directory identity on a person is not that, and a role on a group is not that either. NOTE for the next wave: the five idp-GROUPS rows in this fence still carry the falsified "canon attaches no role to an identity group" clause, and they are deliberately untouched here because on those rows the correction may be a re-SERVE rather than a re-word - `IdpGroup.role_assignments` is RoleAssignment[] and hello-17's groupRole() may now answer it - which is a ruling this wave cannot make alone
refuse — knowledge notes are authored free-text prose a human wrote, the same class of content as session messages, and this universe does not invent authored prose (the standing founder-adopted default; revisit if a client asks)
refuse — knowledge notes are authored free-text prose a human wrote, the same class of content as session messages, and this universe does not invent authored prose (the standing founder-adopted default; revisit if a client asks)
refuse — knowledge notes are authored free-text prose a human wrote, the same class of content as session messages, and this universe does not invent authored prose (the standing founder-adopted default; revisit if a client asks)
refuse — knowledge notes are authored free-text prose a human wrote, the same class of content as session messages, and this universe does not invent authored prose (the standing founder-adopted default; revisit if a client asks)
refuse — knowledge notes are authored free-text prose a human wrote, the same class of content as session messages, and this universe does not invent authored prose (the standing founder-adopted default; revisit if a client asks)
refuse — knowledge notes are authored free-text prose a human wrote, the same class of content as session messages, and this universe does not invent authored prose (the standing founder-adopted default; revisit if a client asks)
SERVED 2026-09-12 (post-hello-18 serving pass). The 2026-09-10 correction on this row named its own expiry: the empty stood because "canon attaches no role to an identity group and binds no person to an external uid", and THE ASK was exactly those two facts. Both have landed - hello-17's features.grants writes one membership row per identity group with scope_kind 'group' (reader.groupRole(), decisions/2026-09-11-2304-hello-17-grants.md), and hello-18's features.externalUid puts person.external_uid on every human (which GitLab's saml/{uid} and scim/{uid} resolve) - and the 2026-09-12 wave that re-worded the idp-USERS rows left the note this answers: "on those rows the correction may be a re-SERVE rather than a re-word". A GROUP IS REGISTERED WITH THIS ENTERPRISE WHEN CANON BINDS IT TO A ROLE: that binding is what registering one MEANS, canon records no Devin-side registration object, and gating all five rows on it is what keeps the three lists answering the same set. This is the ACCOUNT-scoped registry and IdpGroupResponse declares ONE field, the name - no role, no member count, no created instant - so it publishes the same groups the two IdpGroup lists do rather than every directory group: three lists that could disagree about which groups exist would be invariant #5 broken across one family. EVERY MEMBER STILL REPORTS idp_role_assignments: [] AND THAT IS NOT A CONTRADICTION - it is the vendored document's own distinction: IdpRoleAssignment is a role "inherited via IDP group membership" and IdpGroupUser is a user whose "membership is derived from IDP group assignments", both facts about a SEAT, and every Devin seat here is assigned DIRECTLY by the organization (canon's ai_seat), which is the fact the idp-USERS rows were re-ruled onto on 2026-09-12. IdpGroup.role_assignments is what the GROUP confers, not what any member has received. Gated on groupRole(), so on every artifact without features.grants - all 127 registered pins - the lists are Devin's empty envelope and the by-name reads are its 404, byte for byte what these rows served before; conformance asserts that on the hello-7 harness those pins ride.
SERVED 2026-09-12 (post-hello-18 serving pass). The 2026-09-10 correction on this row named its own expiry: the empty stood because "canon attaches no role to an identity group and binds no person to an external uid", and THE ASK was exactly those two facts. Both have landed - hello-17's features.grants writes one membership row per identity group with scope_kind 'group' (reader.groupRole(), decisions/2026-09-11-2304-hello-17-grants.md), and hello-18's features.externalUid puts person.external_uid on every human (which GitLab's saml/{uid} and scim/{uid} resolve) - and the 2026-09-12 wave that re-worded the idp-USERS rows left the note this answers: "on those rows the correction may be a re-SERVE rather than a re-word". A GROUP IS REGISTERED WITH THIS ENTERPRISE WHEN CANON BINDS IT TO A ROLE: that binding is what registering one MEANS, canon records no Devin-side registration object, and gating all five rows on it is what keeps the three lists answering the same set. This row's old reason said "IdpGroup REQUIRES role_assignments, a Devin role the group confers, and canon attaches no role to an identity group at all", and that is the clause hello-17 falsified. ONE ASSIGNMENT PER GROUP, because a group's role is the FLOOR of its members' authority and canon writes exactly one scope_kind 'group' row per group; role_id is the SAME catalog row /v3/enterprise/roles publishes, so the reference resolves beside the answer that produced it; role_type follows canon's permission list (org:admin administers the whole organization, which is what an enterprise-scoped role is) and org_id follows role_type, the rule shapeUser already applies to a member's own assignments. EVERY MEMBER STILL REPORTS idp_role_assignments: [] AND THAT IS NOT A CONTRADICTION - it is the vendored document's own distinction: IdpRoleAssignment is a role "inherited via IDP group membership" and IdpGroupUser is a user whose "membership is derived from IDP group assignments", both facts about a SEAT, and every Devin seat here is assigned DIRECTLY by the organization (canon's ai_seat), which is the fact the idp-USERS rows were re-ruled onto on 2026-09-12. IdpGroup.role_assignments is what the GROUP confers, not what any member has received. Gated on groupRole(), so on every artifact without features.grants - all 127 registered pins - the lists are Devin's empty envelope and the by-name reads are its 404, byte for byte what these rows served before; conformance asserts that on the hello-7 harness those pins ride.
SERVED 2026-09-12 (post-hello-18 serving pass). The 2026-09-10 correction on this row named its own expiry: the empty stood because "canon attaches no role to an identity group and binds no person to an external uid", and THE ASK was exactly those two facts. Both have landed - hello-17's features.grants writes one membership row per identity group with scope_kind 'group' (reader.groupRole(), decisions/2026-09-11-2304-hello-17-grants.md), and hello-18's features.externalUid puts person.external_uid on every human (which GitLab's saml/{uid} and scim/{uid} resolve) - and the 2026-09-12 wave that re-worded the idp-USERS rows left the note this answers: "on those rows the correction may be a re-SERVE rather than a re-word". A GROUP IS REGISTERED WITH THIS ENTERPRISE WHEN CANON BINDS IT TO A ROLE: that binding is what registering one MEANS, canon records no Devin-side registration object, and gating all five rows on it is what keeps the three lists answering the same set. The by-name half, and its refusal was DISSOLVED rather than overruled: the row was refuse because "the IdpGroup list is empty, no name can resolve, and Devin's own 404 is the only possible answer". The list is not empty now, a name it carries answers 200 with the list's own item, and a name it does not carries Devin's ordinary resource 404 - the same status the ruling wanted, produced by the served route rather than asserted by a table, and WITHOUT x-sandboxapis-refusal, which conformance asserts is absent. EVERY MEMBER STILL REPORTS idp_role_assignments: [] AND THAT IS NOT A CONTRADICTION - it is the vendored document's own distinction: IdpRoleAssignment is a role "inherited via IDP group membership" and IdpGroupUser is a user whose "membership is derived from IDP group assignments", both facts about a SEAT, and every Devin seat here is assigned DIRECTLY by the organization (canon's ai_seat), which is the fact the idp-USERS rows were re-ruled onto on 2026-09-12. IdpGroup.role_assignments is what the GROUP confers, not what any member has received. Gated on groupRole(), so on every artifact without features.grants - all 127 registered pins - the lists are Devin's empty envelope and the by-name reads are its 404, byte for byte what these rows served before; conformance asserts that on the hello-7 harness those pins ride.
SERVED 2026-09-12 (post-hello-18 serving pass). The 2026-09-10 correction on this row named its own expiry: the empty stood because "canon attaches no role to an identity group and binds no person to an external uid", and THE ASK was exactly those two facts. Both have landed - hello-17's features.grants writes one membership row per identity group with scope_kind 'group' (reader.groupRole(), decisions/2026-09-11-2304-hello-17-grants.md), and hello-18's features.externalUid puts person.external_uid on every human (which GitLab's saml/{uid} and scim/{uid} resolve) - and the 2026-09-12 wave that re-worded the idp-USERS rows left the note this answers: "on those rows the correction may be a re-SERVE rather than a re-word". A GROUP IS REGISTERED WITH THIS ENTERPRISE WHEN CANON BINDS IT TO A ROLE: that binding is what registering one MEANS, canon records no Devin-side registration object, and gating all five rows on it is what keeps the three lists answering the same set. The org-scoped twin, serving the same object as its enterprise twin: which endpoint you ask does not change what a group may do, the reading shapeOrgUser already takes one resource along. The parent still has to resolve - an {org_id} this universe does not have is Devin's 404 through the shared org wrapper. EVERY MEMBER STILL REPORTS idp_role_assignments: [] AND THAT IS NOT A CONTRADICTION - it is the vendored document's own distinction: IdpRoleAssignment is a role "inherited via IDP group membership" and IdpGroupUser is a user whose "membership is derived from IDP group assignments", both facts about a SEAT, and every Devin seat here is assigned DIRECTLY by the organization (canon's ai_seat), which is the fact the idp-USERS rows were re-ruled onto on 2026-09-12. IdpGroup.role_assignments is what the GROUP confers, not what any member has received. Gated on groupRole(), so on every artifact without features.grants - all 127 registered pins - the lists are Devin's empty envelope and the by-name reads are its 404, byte for byte what these rows served before; conformance asserts that on the hello-7 harness those pins ride.
SERVED 2026-09-12 (post-hello-18 serving pass). The 2026-09-10 correction on this row named its own expiry: the empty stood because "canon attaches no role to an identity group and binds no person to an external uid", and THE ASK was exactly those two facts. Both have landed - hello-17's features.grants writes one membership row per identity group with scope_kind 'group' (reader.groupRole(), decisions/2026-09-11-2304-hello-17-grants.md), and hello-18's features.externalUid puts person.external_uid on every human (which GitLab's saml/{uid} and scim/{uid} resolve) - and the 2026-09-12 wave that re-worded the idp-USERS rows left the note this answers: "on those rows the correction may be a re-SERVE rather than a re-word". A GROUP IS REGISTERED WITH THIS ENTERPRISE WHEN CANON BINDS IT TO A ROLE: that binding is what registering one MEANS, canon records no Devin-side registration object, and gating all five rows on it is what keeps the three lists answering the same set. The org-scoped by-name twin, on the same terms as its enterprise twin: a name the registry carries answers 200 with the list's own item, a name it does not is Devin's ordinary resource 404 with no refusal header, and an {org_id} nobody has is the org wrapper's 404. EVERY MEMBER STILL REPORTS idp_role_assignments: [] AND THAT IS NOT A CONTRADICTION - it is the vendored document's own distinction: IdpRoleAssignment is a role "inherited via IDP group membership" and IdpGroupUser is a user whose "membership is derived from IDP group assignments", both facts about a SEAT, and every Devin seat here is assigned DIRECTLY by the organization (canon's ai_seat), which is the fact the idp-USERS rows were re-ruled onto on 2026-09-12. IdpGroup.role_assignments is what the GROUP confers, not what any member has received. Gated on groupRole(), so on every artifact without features.grants - all 127 registered pins - the lists are Devin's empty envelope and the by-name reads are its 404, byte for byte what these rows served before; conformance asserts that on the hello-7 harness those pins ride.
refuse — an automation INSTANCE exists only because somebody configured one, and this universe accepts no writes (invariant #3); the vendor-fixed half of the surface — schemas and templates — is derived instead
refuse — an automation INSTANCE exists only because somebody configured one, and this universe accepts no writes (invariant #3); the vendor-fixed half of the surface — schemas and templates — is derived instead
derive
derive
nobody here ever wrote a playbook, so the playbook list is empty
the org-scoped playbook twin: nobody here ever wrote a playbook, so the playbook list is empty
refuse — the id half of the playbook pair: nobody here ever wrote a playbook, so the playbook list is empty, so no id can resolve and Devin's own 404 is the only possible answer (the by-id shape ruled in DECISIONS 2026-09-01 [coverage/A0])
refuse — the id half of the playbook pair: nobody here ever wrote a playbook, so the playbook list is empty, so no id can resolve and Devin's own 404 is the only possible answer (the by-id shape ruled in DECISIONS 2026-09-01 [coverage/A0])
this universe's only non-human identity is the CI bot and it holds no Devin seat, so the service-user list is empty
the org-scoped service-user twin: this universe's only non-human identity is the CI bot and it holds no Devin seat, so the service-user list is empty
refuse — the id half of the service-user pair: this universe's only non-human identity is the CI bot and it holds no Devin seat, so the service-user list is empty, so no id can resolve and Devin's own 404 is the only possible answer (the by-id shape ruled in DECISIONS 2026-09-01 [coverage/A0])
refuse — the id half of the service-user pair: this universe's only non-human identity is the CI bot and it holds no Devin seat, so the service-user list is empty, so no id can resolve and Devin's own 404 is the only possible answer (the by-id shape ruled in DECISIONS 2026-09-01 [coverage/A0])
refuse — the git-provider connection is an install-time OAuth object on Devin's side, and this universe's repositories are served by our own GitHub/GitLab mirrors rather than through a Devin-held provider grant
refuse — the git-provider connection is an install-time OAuth object on Devin's side, and this universe's repositories are served by our own GitHub/GitLab mirrors rather than through a Devin-held provider grant
refuse — the git-provider connection is an install-time OAuth object on Devin's side, and this universe's repositories are served by our own GitHub/GitLab mirrors rather than through a Devin-held provider grant
SERVED 2026-09-12: RepositoryResponse over repo/commits/git_tree_entry. provider_repository_id is githubId(repo.id) - the very id the GitHub replica publishes for the same repository - and the connection host is github.com, which every Devin session's own pr_url already names. The connection id is required with no column and is MINTED deterministically from the ORGANIZATION, because this enterprise has exactly one connection and every repository is read through it; a per-repository id would claim as many connections as repositories. indexing_status is nullable and is null: canon carries no Devin index
SERVED 2026-09-12: RepositoryIndexingResponse requires repository_path, indexing_enabled AND branches (the audit's `repository_path alone` read RepositoryIndexingItem, which is the BULK-INDEX REQUEST schema, not this response). indexing_enabled is false and branches is empty as a MEASUREMENT rather than a default - Devin repository indexing is a setting written through the API, canon carries no Devin index, no index job and no indexing setting, and this universe accepts no writes, so nothing ever enabled it and no branch is configured for it. Conformance asserts both, so the day canon grows an index the row reds instead of carrying a stale false
derive
generate — A WIDENING, NOT A BUILD: canon.AdminAuditEvent exists (packages/canon/src/support-desk.ts:690) and its own doc comment calls it "GENERIC by construction". The action vocabulary (AuditAction, :602), the change vocabulary (AuditChangeSlot, :633), the target vocabulary (AuditTargetKind, :611) and the RFC 5737 source-address rule (AuditSourceBlock, :652) all shipped, and Zendesk serves the table today (reader.listAuditEvents(), ruled 2026-09-07). TWO things are missing and neither is the entity: git-host members on AuditTargetKind, and a generation that WRITES personActor — the field is declared for exactly this wave and "this generation writes none" (:695-697)
generate — A WIDENING, NOT A BUILD: canon.AdminAuditEvent exists (packages/canon/src/support-desk.ts:690) and its own doc comment calls it "GENERIC by construction". The action vocabulary (AuditAction, :602), the change vocabulary (AuditChangeSlot, :633), the target vocabulary (AuditTargetKind, :611) and the RFC 5737 source-address rule (AuditSourceBlock, :652) all shipped, and Zendesk serves the table today (reader.listAuditEvents(), ruled 2026-09-07). TWO things are missing and neither is the entity: git-host members on AuditTargetKind, and a generation that WRITES personActor — the field is declared for exactly this wave and "this generation writes none" (:695-697)
refuse — guardrails are an enterprise-security beta this org never turned on, and Devin answers 403 to guardrail-violation reads for an enterprise with no guardrail policy configured
refuse — guardrails are an enterprise-security beta this org never turned on, and Devin answers 403 to guardrail-violation reads for an enterprise with no guardrail policy configured
SERVED 2026-09-12: OrganizationResponse over the canonical `org` row this host already resolves {org_id} against. created_at is reader.orgCreatedEpoch(), updated_at the newest canonical event in any of the organization's repositories, and BOTH ACU limits are declared integer|null and are null - this enterprise buys ACUs and spends them (the consumption family reports what was spent) and nobody here capped a session or a cycle, which is the same fact /v3/enterprise/consumption/acu-limits/devin already answers empty
SERVED 2026-09-12: the single read of the same OrganizationResponse the list publishes, through the shared {org_id} resolution - so an id this universe does not have is Devin's own 404
refuse — no Devin Review exists in this universe - a review record is created by a trigger through the API and this universe accepts no writes (invariant #3), and canon's ai_pull rows attribute AUTHORSHIP rather than review (the two pulls Devin produced were reviewed by people, person-14 and person-04); Devin's own answer when no review exists for the resolved commit is its 404, which the operation's own description states. RULED by the founder 2026-09-12
refuse — the org-scoped Devin Review twin, and the same fact: no Devin Review exists in this universe - a review record is created by a trigger through the API and this universe accepts no writes (invariant #3), and canon's ai_pull rows attribute AUTHORSHIP rather than review (the two pulls Devin produced were reviewed by people, person-14 and person-04); Devin's own answer when no review exists for the resolved commit is its 404, which the operation's own description states. RULED by the founder 2026-09-12
SERVED 2026-09-10 (hello-16): reader.listMembershipRoles() (packages/artifact/src/reader.ts:3042) in Devin's PaginatedResponse[Role] envelope. `role_type` is REQUIRED with a closed enterprise|org enum canon does not carry, so it is READ OFF the permission list canon does: a role holding `org:admin` administers the whole organization, everything below it acts inside one org — conformance asserts the derivation rather than the current five answers. THIS ROW IS WHAT MAKES THE MEMBERS SURFACES RESOLVE: every RoleAssignment.role they publish carries a role_id this operation must answer, so from hello-16 on both read this one catalog and conformance asserts that every role a member holds is in it
SERVED 2026-09-10 (hello-16): the same row plus canon's permission list — reader.listMembershipRoles() (reader.ts:3042) — which is the whole difference the operation declares ("Get a single role AND ITS PERMISSIONS"). `permissions` is canon's closed `scope:level` vocabulary, which the schema types `array<string>` with no enum, so re-spelling it into Devin permission identifiers would be a table nothing in canon could hold us to. A role id the catalog does not carry is Devin's own problem+json 404, never the coverage miss
refuse — a schedule exists only because somebody created one, and this universe accepts no writes (invariant #3) — nobody here scheduled a recurring Devin run
refuse — a schedule exists only because somebody created one, and this universe accepts no writes (invariant #3) — nobody here scheduled a recurring Devin run
this org never adopted tagging, which is the same fact the already-ruled session `tags` rows carry (DECISIONS 2026-08-20 [devin])
with no organization tag defined there is no default tag set either — the twin of the org-tags row. Devin's DefaultTagResponse is a nullable SCALAR, so this row's empty shape is `default_tag: null` (empty-shape.ts NULL_KEYS), not an empty list
refuse — an enterprise infrastructure/security administration read, and the sandbox token is an enterprise-member token rather than a superadmin one; Devin answers 403 (the ruled instance-admin precedent, coverage/reviews/2026-09-01-jira-modes.md)
refuse — CORRECTED 2026-09-12 (hello-18 serving wave W-c): the reason this row carried said the artifact carries no file data, and hello-15 made that FALSE - attachment.body holds real bytes for 35 rows and renderer-zendesk serves them. The refusal still stands, on two facts that ARE this row's own. FIRST, no uuid this route could resolve is ever advertised: no generation attaches an attachment to an agent session, .../sessions/{devin_id}/attachments answers [] on every session, so this organization has no attachment with that uuid and Devin's own 404 is the whole answer. SECOND, the SUCCESS answer the document declares is a 307 to an IP-enforcing proxy or a presigned S3 URL - a link into storage this replica has no equivalent of - so even with bytes to serve this row could not answer 307, which is the ruled avatar-bytes precedent (coverage/reviews/2026-09-01-jira-modes.md row 6) applied to a redirect rather than to a body
this enterprise configures no per-seat ACU cap; any figure printed here would be a placeholder copied off the spec example, which invariant #4 forbids (the ruled OpenAI rate_limits shape, DECISIONS 2026-08-20 [P31/openai])
refuse — an enterprise infrastructure/security administration read, and the sandbox token is an enterprise-member token rather than a superadmin one; Devin answers 403 (the ruled instance-admin precedent, coverage/reviews/2026-09-01-jira-modes.md)
refuse — an enterprise infrastructure/security administration read, and the sandbox token is an enterprise-member token rather than a superadmin one; Devin answers 403 (the ruled instance-admin precedent, coverage/reviews/2026-09-01-jira-modes.md)
SERVED 2026-09-12: QueueResponse MEASURED off the fleet rather than asserted - queue_size is the sessions this artifact left unfinished, read through the same status map /v3/enterprise/sessions publishes on every session, and it is 0 at `normal` because every Devin session in canon is terminal (hello-17: 65 completed, 2 failed, no running row). It goes non-zero on its own the day the generator leaves a session in flight
SERVED 2026-09-12 (hello-19 `ciBindings`): the org-level secrets this enterprise hands its CODING AGENT, off reader.orgCiBindings(org, 'coding-agent'). The 2026-09-01 reason asked for a new canon entity ActionsSecret, and hello-19's `ci_binding` IS that entity canonically — one named thing handed to something that runs, with one boolean saying whether its value may be published. THE `coding-agent` AUDIENCE AND NOTHING ELSE: Devin IS the coding agent, while `pipeline` is the build system's audience (GitLab's CI/CD variables, GitHub's Actions secrets) and `dependency-bot` is the updater's — every ci_binding accessor names a scope AND an audience precisely so a renderer has no call it could forget to filter, and conformance asserts that no other audience's key appears here. BOTH ROWS ARE SERVED RATHER THAN THE MASKED HALF, because SecretResponse requires `is_sensitive` as a boolean: Devin's store holds the agent's model key and the workspace it operates in alike, so is_sensitive IS the canonical `masked` column and both values appear. No value is published because SecretResponse declares no value field — the same shape canon holds for a masked binding, which is the argument the whole domain rests on. `secret_id` is minted from the canonical row id (the devinApiKeyId rule); `key` is the name the theme wrote; `note` is null because canon records none; `created_by` is the enterprise administrator's Devin user id, which /v3/enterprise/members/users/{user_id} resolves (invariant #5), and the list is empty rather than naming nobody on an artifact with no Devin seat; `created_at` is the binding's own instant; `secret_type` is key-value and `access_type` is org; `updated_at`/`updated_by` are null exactly when `updated_epoch` equals `created_epoch`, which is canon's own record of never rotated. Gated on hasCiBindings(); an older artifact answers the named hello-19 CI-binding gap, because [] there would say this enterprise hands its agent nothing while the same artifact publishes the sessions that agent ran
SERVED 2026-09-12 on hello-17: PatUserSelf for the founding member - the same `athena` GET /user and GitLab's currentUser resolve to. api_key_id and api_key_name read off a REAL person_credential row (kind personal-token, state active), whose title IS the key's name, and user_id is a Devin member id /v3/enterprise/members/users/{user_id} resolves. Both halves need hello-17 (callerCredentials for the token, adminSeat for the owner's Devin seat); an older generation is NAMED rather than answered with a minted key
03 / What's simulated
Every Devin call resolves against the same simulated data set every other provider serves. Counted from the devin-v3-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.
This vendor's user roster is disjoint from the GitHub Copilot cohort and from Anthropic's, Cursor's and OpenAI's — nobody holds two vendors' seats — and equals its own slice of the canonical seat table.
Against GitHub, Anthropic, Cursor, OpenAI · parity/m3-parity.test.ts
Every identity this host bills for resolves as a user on the GitHub host with the same name, and the session count here plus Cursor's agents plus GitHub's own agent tasks sum to the whole canonical agent-session table.
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.
devin-v3.snap.sandboxapis.devgeneration 5v3serveddevin-v3-g10.snap.sandboxapis.devgeneration 10v3serveddevin-v3-g11.snap.sandboxapis.devgeneration 11v3serveddevin-v3-g6.snap.sandboxapis.devgeneration 6v3serveddevin-v3-g8.snap.sandboxapis.devgeneration 8v3serveddevin-v3-g9.snap.sandboxapis.devgeneration 9v3servedNeed an endpoint that is not served yet?
Every row Devin'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.