AGENTS.md@api/openapi · git:20261020.8d47d7a · 2026-10-20 · sha256 aa531632becfea0a
AGENTS.md@api/openapi git:20261020.8d47d7aA
Immutable. This exact content never changes and is served at /api/v1/blob/aa531632becfea0a.
# Agent Instructions — `api/openapi`
The OpenAPI 3.1 sources, the contract of record for the HTTP API. Layout,
bundling, the ogen `oneOf`-null workaround and pagination how-tos are in
[`readme.md`](readme.md); read it before restructuring spec files.
## The generate loop
- Never hand-edit `api/generated/` (ogen) or `packages/api/src/generated/`
(orval). Change the spec, then run `moon run server:generate`; CI checks the
committed output with `server:check-generate`.
- Lint the spec with `moon run server:openapi` (Redocly with the repo's
[`redocly.yaml`](../../redocly.yaml)).
## Wire casing: snake_case
Wire fields, enum values and parameters are `snake_case`. Wrong casing fails
silently at runtime: an unknown property is dropped, not rejected.
- `rule/wire-field-snake-case`, `rule/wire-enum-no-camel-case` and
`rule/wire-parameter-no-camel-case` in `redocly.yaml` gate it, with
documented carve-outs (JSON Schema keywords, `x-` extensions, RFC
vocabularies, header and cookie names).
- `moon run workspace:check-openapi-rules` runs fixture tests
(`scripts/openapi-casing-rules.test.mjs`) that fail when a rule silently
stops matching. Keep the fixtures in step with the rules.
## Error codes
Errors are `{ code, message, details? }`
([`components/error-details.yaml`](components/error-details.yaml)). Codes are
stable, dot-namespaced (`att.invalid_proof`), and what clients branch on. The
taxonomy and producer contract:
[ADR 030](../../docs/adrs/030-error-model-mapping-and-reporting.md) and
[`error-details-producers.md`](../../docs/design/api/error-details-producers.md).
- `components/schemas/errors/` holds one schema per error code, generated by
`gen_error_schemas` from the domain errors.
- Each operation's error set is `endpoints/**/<operationId>-error-response.yaml`,
generated by `gen_openapi_errors` from what the implementation can return.
- Never hand-edit either.
## Pagination and list queries
New endpoints are cursor-based (`page_token` / `next_page_token`, `limit`
default 20, max 100), and list queries are `POST /<resource>/query` with
structured `filter` / `sorting` bodies
([ADR 027](../../docs/adrs/027-cursor-based-pagination.md),
[ADR 031](../../docs/adrs/031-openapi-querying.md)). `POST /sessions/query` is
the reference implementation; the how-to is in
[`readme.md`](readme.md#api-design-conventions).
## Identifiers on the wire
Resource IDs are prefixed opaque strings (`user_01H…`, `proj_01H…`,
[ADR 047](../../docs/adrs/047-dialect-id-generation.md)); ephemeral IDs follow
[ADR 012](../../docs/adrs/012-ephemeral-id-api-representation.md). Use the
prefixed form in `example:` fields, never the retired dictionary slug
(`river-8421`).
## CLI commands — `x-zitadel-cli`
An operation the `zitadel` CLI runs names its command in `x-zitadel-cli`, on
the line after its `operationId` (`x-zitadel-cli: users create`); the API
reference shows it beside the samples. The CLI's
[`api-coverage` test](../../apps/cli/tests/unit/commands/api-coverage.test.ts)
fails until every command names exactly one operation it calls, and on a name
no command answers to.
## Descriptions — the first sentence stands alone
Every operation and webhook has a `description`, the body of its API reference
page. Its first sentence, as plain text, is the page's description everywhere
else: the metadata and Open Graph image (cut at a word past 160 characters),
`llms.txt` and the MCP server's page list
([`apps/docs/lib/description.ts`](../../apps/docs/lib/description.ts)).
- Open with a sentence that says on its own what the operation does, in 160
characters or fewer.
- `rule/operation-description-opens-with-sentence` rejects a description that
starts with a heading, a quote (a `> **Deprecated**` note goes after the
sentence), a list, a table, a code fence or HTML.