Resource design sheets

Generated from docs/api_standards/resources/README.md. Edit that file, then regenerate: python docs/portfolio/_build/generate_reference_pages.py.


<!– The inventory is linked by absolute URL, not a relative path: this file is read in the repository and published as the handbook’s resource-sheets index, and no relative path is correct in both. –>

One page per resource family, written before the handlers, reviewed as the design.

WP-6 of docs/plans/20260811_documentation_system_execution.md, from docs/proposals/20260811_rest_api_documentation.md (R3).

Why these exist

An OpenAPI document is a reference, not a design. KAP’s is 7,559 lines and can tell you the shape of every request while answering none of the questions a reviewer actually asks:

  • What is this resource, and what are its states?
  • What happens on the second identical call?
  • What does a client do with a 409?
  • Who may see it, and what do they get if they may not?

Those answers decide whether the handlers are right, and none of them fit in a schema. A sheet is where they go, one per resource family — see the API Endpoint Inventory for the families and how many operations each holds.

The count is deliberately not restated here. It was, and said 125 while the specification said 147 — invisible for months because the check that guards these numbers read one line at a time and the claim wrapped across two.

A sheet is not a second copy of the spec. It never restates request or response shapes; the spec owns those and is generated. If a sheet and the spec disagree about a shape, the sheet has overstepped.

The nine sections

Section What it must state
Resource What it is in the domain, and its identity — which id, which scope it lives under
Lifecycle The states and the legal transitions. A resource with no states says so
Operations method · path · purpose · auth · idempotent? · emits event?
Access Which CONTRACT-API-001 case applies, and the not-permitted response
Errors Each code this family returns and what the client should do
Pagination & limits Only if it returns collections; the ceiling, and behaviour past it
Consistency What a caller may assume after a write returns 2xx
Agent notes Whether an AI engine may call it, and what a model needs told that a human would not
Rejected shapes The one or two obvious alternatives and why not

The last section is the one that makes a sheet re-readable in a year. A design without its rejected alternatives reads as arbitrary, and the next person re-proposes what was already decided against.

Frontmatter — the part a machine checks

---
resource: annotation_suggestions
access_case: dataset-scoped
operations:
  - listImageAnnotationSuggestions
error_codes:
  - image.not_found
---

Checked by ai/tests/ci/test_resource_sheets.py:

  • every operations: entry is a real operationId in swagger.yaml — a sheet describing an operation that does not exist is the failure mode a prose document cannot detect on its own;
  • every error_codes: entry is in docs/api_standards/error_codes.yaml;
  • access_case is from the closed set below.

Prose is not checked, and pretending otherwise would be worse than not checking. The frontmatter is the part that can go quietly wrong.

access_case

Value Meaning Not-permitted response
public No identity required —
owner-only Only the row’s owner 404
org-scoped Any member of the owning organization 404
dataset-scoped Anyone who can see the parent dataset 404
admin Platform administrators 403

404, not 403, for everything scoped. A 403 tells a caller the resource exists and is not theirs, which for a tenant-scoped resource is a disclosure. CONTRACT-API-001 owns this rule and its one named exception; the sheet declares which case applies, never redefines it.

When a sheet is required

  • New resource family — yes, before the handlers.
  • Breaking change to an existing family — yes, amend the sheet in the same change.
  • Adding an operation to a family that has one — update the operations: list; the prose usually does not move.
  • Adding an operation to a family with no sheet — not required. Backfilling 25 sheets by reading handlers would document what the code does, which the generated spec already does for free.