API & Clients
The backend exposes a REST API built with Django REST Framework. This page covers the endpoints, authentication, the OpenAPI schema, and the generated SDKs.
Exploring the API
With the server running (see Getting Started):
http://localhost:8000/— the browsable API roothttp://localhost:8000/schema/swagger/— Swagger UIhttp://localhost:8000/schema/redoc/— ReDochttp://localhost:8000/schema/— the raw OpenAPI document
Responses use camelCase keys (a middleware converts Django's snake_case), which is what the generated clients and the frontend expect.
Endpoints
Routes are wired in backend/spellbook/urls.py, backend/website/urls.py, and the project urls.py.
Core (spellbook)
| Endpoint | Description |
|---|---|
GET /variants/ |
The generated variants — the main read endpoint. Supports the search query language. |
GET /cards/ |
Cards. |
GET /features/ |
Features. |
GET /templates/ |
Templates. |
GET/POST /find-my-combos |
Given a decklist, returns the combos it can assemble (the engine's up phase). |
GET/POST /estimate-bracket |
Estimates the power bracket of a decklist. |
… /variant-suggestions/ |
Community-submitted combos awaiting review. |
… /variant-update-suggestions/ |
Suggested edits to existing variants. |
… /variant-aliases/ |
Redirects from alternative ids to canonical variants. |
Site support (website)
| Endpoint | Description |
|---|---|
GET /properties/ |
Site-wide configurable properties. |
GET /card-list-from-url |
Parse a decklist from a supported deckbuilder URL (Moxfield, Archidekt, Deckstats, TappedOut). |
GET/POST /card-list-from-text |
Parse a decklist from pasted text. |
Users & auth
/users/, plus the authentication endpoints below.
Authentication
Two mechanisms, both configured in the project urls.py:
- JWT (
simplejwt): POST /token/— obtain an access/refresh pairPOST /token/refresh/— refresh an access tokenPOST /token/verify/— verify a token
Send the access token as Authorization: Bearer <token>.
- Social login (social-auth) — Discord OAuth, enabled when DISCORD_CLIENTID / DISCORD_CLIENTSECRET are set.
Most read endpoints are public; writing and reviewing require authentication and the appropriate permissions. Editors work primarily through the admin panel (/admin), not the API.
The search query language
variants (and template matching) accept a Scryfall-style search query — e.g. ci:temur mana result:"infinite mana". The grammar is defined with Lark in spellbook/parsers/ and turned into ORM filters by the transformers in spellbook/transformers/. Extend the query language by editing the .lark grammar and its transformer together.
OpenAPI schema
The schema is generated from the code by drf-spectacular. It is the contract the clients and frontend depend on, so keep it accurate: add serializer annotations and @extend_schema hints when you add or change an endpoint.
Regenerate the committed schema with:
cd client
./generate-openapi.sh # writes client/openapi.yaml
The script runs manage.py spectacular … --fail-on-warn --validate, so a schema warning is treated as an error — the CI does the same.
Generated clients
The SDKs are generated from openapi.yaml with openapi-generator (run via Docker, so Docker must be running):
cd client
./generate-openapi.sh # 1. refresh the schema
./generate-client-python.sh # 2a. Python client -> client/python/
./generate-client-typescript.sh # 2b. TypeScript client -> client/typescript/
- Python — package
spellbook_client(async,asynciolibrary). Used by the bots and the Python integration tests. - TypeScript — published to npm as
@space-cow-media/spellbook-clientand consumed by the React frontend.
The CI regenerates and publishes both on release; you only need to run these locally when changing the API and testing a client against it.