Schemas and Contracts Guide
Overview
Where request/response schemas live and how to change them without breaking callers.
Where schemas live
- Request/response schemas:
app/schemas/ - Error schemas:
app/schemas/errors.py - OpenAPI examples:
app/openapi/examples/ - Router contract (
responses, summary, description):app/api/v1/
Contract rules
- API contract is a public interface.
- Existing fields and semantics are backward-compatible within the same major.
- Error
codeandkeysemantics are immutable. - Changes are additive by default.
- Critical write endpoints define idempotency contract via
Idempotency-Keyheader.
Idempotency contract for writes
- Same key + same body → return the same success response as the first call.
- Same key + different body → return a clear conflict response.
- Missing key when required → return a clear validation error.
Error contract
Business error shape:
{"code":"...","key":"...","message":"...","source":"business"}
Validation error shape:
{"error_type":"validation_error","endpoint":"...","errors":[...]}
Validation rules source:
app/validation/- Status-first governance matrix: Error Matrix By Status
How to evolve schemas
- Update schema classes in
app/schemas/. - Update endpoint declaration in
app/api/v1/.... - For write routes, update header contract and idempotency behavior docs.
- Update examples in
app/openapi/examples/.... - Update validation mapping in
app/validation/...if needed. - Add or update tests.
- Run
make verifyandmake docs-fix.
Anti-patterns
- Copy-pasting the same examples in many routers instead of one source.
- Reusing an error
codefor a new meaning. - Breaking behavior without a version bump or migration note.
- Accepting retried writes without documented idempotency rules.
Page history
| Date | Change | Author |
|---|---|---|
| Added Page history section (repository baseline). | Ivan Boyarkin |