/v1 surface.
Base URL
The spec declares a single server relative to the backend’s deployed origin, so all paths are under that origin:GET /openapi.json serves the live spec and GET /docs serves Swagger UI over it.
The success envelope
Domain routes return their payload as raw JSON — there is no{ data: … }
wrapper. A read returns the read model (an object, or a page for a list); a create
returns 201 with the created resource.
The one exception is
GET /v1/auth/whoami, a newer route that does wrap its payload
in { data: … }. Unifying every success response under a single { data } envelope
is deliberately out of scope — it would be a breaking change to the API client and
both apps.The error envelope
Every non-2xx response returns the same failure envelope:code— a machine-readable kernelResultCode. Branch on this, not onmessage.message— a human-readable explanation, safe to surface; it never leaks server internals.statusCode— the HTTP status, mirrored into the body so you need not read the headers.
VALIDATION_FAILED) adds a fieldErrors array, one entry per
rejected field:
code → status map.
Pagination
List operations page withpage and pageSize query parameters and return a page
read model. Request the next page by incrementing page:
Validation
Request bodies, path params and query params are validated at runtime from the same schemas that generate the spec, so a request that does not match the documented shape is rejected with the standardVALIDATION_FAILED envelope — the documentation and the
validator can never drift apart.
