API

JSON over the same engines the web app uses. Authenticate with a key from your account — a key acts as one learner and can read nobody else's data.

Authentication

curl -s https://praxis.sabiapps.com/api/progress \
  -H "Authorization: Bearer prx_your_key_here"

x-api-key: prx_… works too. Key-authenticated requests skip CSRF; session requests from the browser do not.

GET /api/curriculum Subjects, counts, and any validation problems found at boot.
GET /api/subjects/:id One subject with its skills and every item's calibrated difficulty.
GET /api/skills/:id A skill, your mastery under BKT, and the expected number of further attempts to reach 0.95.
POST /api/next The next question — plus the selector's full reasoning: score components, predicted success under both models, and the runners-up it passed over.
POST /api/attempts Submit an answer. Grades it, updates BKT / Elo / SM-2 in one transaction, and returns what every model did.
POST /api/hint The next rung of the hint ladder. Never returns anything above the level you have reached.
POST /api/practice-set A whole set in one call, for clients that prefer to batch.
GET /api/plan Today's study plan: review, then practice, then one stretch item — with the reasoning for each block.
GET /api/review The SM-2 queue, ordered by how overdue each card is relative to its own interval.
GET /api/progress Totals, streak, mastery, recurring misconceptions, and 30 days of daily activity.
GET /api/calibration Brier scores and reliability buckets for both models. The honest answer to "is any of this working?".
GET /api/items/drift Items whose measured difficulty has moved furthest from their author's estimate.
GET /healthz Liveness, database driver, resolved model provider.

A full loop

# Ask for a question, and why it was chosen
curl -s -X POST https://praxis.sabiapps.com/api/next \
  -H "Authorization: Bearer $PRAXIS_KEY" -H 'content-type: application/json' \
  -d '{"subject":"algebra"}'

# Answer it. Pass back the choiceMap you were given for a multiple-choice item —
# the choices are shuffled per request so position carries no signal.
curl -s -X POST https://praxis.sabiapps.com/api/attempts \
  -H "Authorization: Bearer $PRAXIS_KEY" -H 'content-type: application/json' \
  -d '{"itemId":"alg-lin-1","response":"5","hintsUsed":0,"seconds":31}'

# Check whether the predictions are worth anything
curl -s https://praxis.sabiapps.com/api/calibration -H "Authorization: Bearer $PRAXIS_KEY"

Currently serving 170 questions across 38 skills in 5 subjects.

Errors

Every error is { "error": { "code", "message", "details", "requestId" } } with a matching HTTP status. Rate limits return 429 with a Retry-After header; model-backed endpoints have their own, tighter limit.