Skip to content

The API

345 operations across 279 paths, in 31 tag groups.

  • Reference — every endpoint, rendered from the OpenAPI document with Scalar.

  • The API as a contract — addressing, versioning, the error envelope, tokens, rate limits, and the two rules a client should not try to work around.

The tag groups

access · admin · ai-mode · articles · attempts · auth · categories · collections · contact · documents · drafts · exam results · exams · favorites · feedback · flashcards · folders · glossary · health · media · public · question-categories · questions · quizzes · search · share · study-plans · study-tools · tags · teach · tts

Addressing

Everything is under /api/v1. /api/… reaches the same route and always will — it is the address the web application in this repository was written against, and an address that has shipped is a promise.

It is a rewrite, not a second mount, so the two cannot drift and the OpenAPI document describes each endpoint once rather than twice.

Two addresses sit outside the scheme deliberately:

Path Why
/api/health Monitoring points at it and nobody edits that for a year. It also answers at /api/v1/health.
/uploads/… An image's URL gets written into markdown and shared. Those must not move.

The live documents

The running application serves its own:

  • GET /api/openapi.json — the schema
  • /api/docs — Swagger UI
  • /api/redoc — ReDoc

The copy in this site is a committed snapshot, so the documentation renders without a backend behind it. Regenerate it whenever a route changes:

docker exec -w /app -e PYTHONPATH=/app quiz-backend-1 \
  python -c "import json;from app.main import app;print(json.dumps(app.openapi()))" \
  > docs/openapi.json

Authentication

Every endpoint but /api/health, /api/v1/public/…, the share routes and the SSO handshake needs a bearer token.

Authorization: Bearer <access token>

Get one through the SSO exchange. There is no password grant — POST /auth/login answers 410.