API Structure
FastAPI routers live under app/routers/, one resource family per module (test.py, test_set.py,
metric.py, endpoint.py, architect.py, …). Paths follow REST conventions unless noted below.
Authentication backstop
There is no per-route auth declaration to remember. After all routers are included (core and EE),
main.py walks every registered route and injects authentication and authorization dependencies:
apply_auth_backstopinjectsrequire_current_user_or_tokenon every route whose exact path is not inPUBLIC_ROUTES, unless the route already declares an auth dependency (directly or transitively viaget_tenant_db_session). This guarantees a route is never accidentally exposed.apply_authz_backstopthen injects arequire_permission(capability)check on every non-exempt route.
PUBLIC_ROUTES (no authentication) lives in app/auth/public_routes.py:
The check is an exact match against the fully-resolved path, so trailing slashes matter. The list is a mutable module attribute so EE features can extend it from their bootstrap before their routers are included.
For token vs. session auth and the RBAC capability model, see Backend Authentication and Authorization (RBAC).
Standard endpoints
Most resource routers follow the same shape:
GET /\{resource\}/— list (filtering, sorting, pagination; total inX-Total-Count)GET /\{resource\}/\{id\}— get one by IDPOST /\{resource\}/— createPUT /\{resource\}/\{id\}— updateDELETE /\{resource\}/\{id\}— delete
Query parameters
List endpoints accept skip, limit, sort_by, sort_order, and OData-style $filter and
$select:
The list endpoint sets X-Total-Count with the unpaginated total (exposed via CORS). See the
OData Query Guide for the full filter syntax.
API documentation
FastAPI generates OpenAPI docs automatically: Swagger UI at /docs, ReDoc at /redoc, and the
schema at /openapi.json.
Error handling
Errors use standard HTTP status codes (400, 401, 403, 404, 500) with a JSON body carrying
a detail message.