You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Generated by reading the routers in backend/app/api/v1/ and the app's route table (app/main.py, app/api/v1/router.py).
In development the live schema is at http://localhost:8000/openapi.json and /docs (both are off in production, DOCS_ENABLED).
All routes below are under /api/v1 unless marked otherwise. Browsers reach them through the Next.js proxy (http://localhost:3000/api/v1/...).
Conventions
Topic
Rule
Auth
Cookies qs_access / qs_refresh / qs_csrf set by POST /auth/login; scripts may send Authorization: Bearer <access_token> (then no CSRF). There is no X-User-Id header (it only exists inside the test client helper).
CSRF
Cookie-authenticated POST/PATCH/PUT/DELETE must send X-CSRF-Token equal to the qs_csrf cookie, from an allowed Origin (FRONTEND_URL / CORS_ORIGINS). Missing or wrong: 403.
Access column
Public no session; User any signed-in active user; Admin role admin; Scoped User, but the resource is loaded through the owner/collaborator check (app/authz.py), so someone else's id answers 404; Owner like Scoped but only the chart owner (collaborators get 403 owner_only).
Idempotency-Key header on POST /evaluations/ai/jobs (replay returns the stored result).
Rate limits
429 with Retry-After. Per IP: login, signup, forgot/reset, refresh, register-user. Per user: chat, AI jobs, uploads, export / bulk-delete, admin, share. Share/invite lookups per sender (RATE_LIMIT_SHARE_LOOKUP, lookup_rate_limited). Redis-backed when REDIS_URL is set, in-memory otherwise.
Pagination
Evaluations: keyset cursor (GET /evaluations/page); notifications and activity: keyset cursor.
Error codes
Status
Meaning / detail.code
400
Invalid or expired reset / verification token
401
Not authenticated, expired access token, rejected refresh (cookies are cleared)
403
CSRF / origin failure; wrong bootstrap token; Administrator access required; owner_only (a collaborator tried an owner action)
404
Not found or not yours (never 403 for foreign data); closed endpoints (register-user after bootstrap); user_not_found when sharing (deliberate)
409
stale_edit; user_job_active (one evaluation job per user); user_chat_active (one chat turn per user); turn_in_progress (session already running a turn); already_collaborator; already_shared; already_saved; not_pending; chart_in_trash; last_admin / self_action (admin rules); weight-sum or other DB conflicts
422
Validation (Pydantic), self_invite, invalid_username, bad Drive URL (per-item detail)
429
Rate limited, or account locked after failed logins
503
Evaluation AWS stack not configured; /ready when Postgres is down
Auth and profile
Method
Path
Access
Purpose
POST
/auth/signup
Public (only if SIGNUP_ENABLED)
Create an account, start a session, send a verification mail
POST
/auth/login
Public
Email, username or email local-part + password; remember_me makes a 30-day refresh cookie
POST
/auth/refresh
Refresh cookie
Rotate the refresh token, issue a new access token
POST
/auth/logout
Cookie
End this browser session (works with an expired access token)
POST
/auth/logout-all
User
Revoke every session of the user
GET
/auth/me
User
Current user
GET
/auth/config
Public
What sign-in pages need: signup_enabled, setup_required, username_login
POST
/auth/register-user
Admin, or X-Bootstrap-Token while zero users exist
Create an account without signup / create the first admin
POST
/auth/forgot-password
Public
Same answer whether or not the address exists (202)
POST
/auth/reset-password
Public
Set a new password from the emailed token
POST
/auth/verify-email
Public
Confirm the emailed token
POST
/auth/resend-verification
User
Send a new verification mail (202)
GET
/auth/providers
Public
OAuth providers and whether each is configured
GET
/auth/oauth/{provider_id}/start
Public
Begin Google / GitHub / Microsoft sign-in (501 until configured)
GET
/auth/oauth/{provider_id}/callback
Public
OAuth redirect target
GET / PATCH
/me
User
Read / update own profile (role is never accepted)
POST
/me/password
User
Change own password (needs the current one)
GET
/me/slots
User
Which of the caller's two slots (evaluation job, chat turn) are busy
Admin users
All Admin, rate limited (RATE_LIMIT_ADMIN), audit logged. Rules: never yourself, never the last active admin (last_admin).
Method
Path
Purpose
GET
/admin/users
List / search accounts
POST
/admin/users
Create a user (admin or user)
PATCH
/admin/users/{user_id}
Change name, email, username, role
POST
/admin/users/{user_id}/password
Set a password (revokes the user's sessions)
POST
/admin/users/{user_id}/deactivate
Block sign-in, revoke sessions, cancel running jobs
POST
/admin/users/{user_id}/reactivate
Allow sign-in again
DELETE
/admin/users/{user_id}
Anonymise: tombstone row, private data removed, shared charts transferred
Scorecards, versions, KPIs
Method
Path
Access
Purpose
GET / POST
/scorecards
Scoped
List charts you own or collaborate on / create
POST
/scorecards/suggest-similar
User
Rank existing charts worth reusing for a free-text request (pgvector)
POST
/scorecards/validate-formula
User
Validate a scoring-formula draft
GET / PATCH
/scorecards/{scorecard_id}
Scoped
Read / update (If-Match)
DELETE
/scorecards/{scorecard_id}
Scoped
Owner: move to trash (204). Collaborator: leave the chart (204)