Skip to main content

Courses API

The course catalog at /courses. Mounted at /api/courses. A course is an external interactive-lecture URL plus AI-extracted metadata, ratings, and comments. Shipped 2026-08-31 (SHAN-437).

Model

  • Categories: math physics computer-science engineering biology chemistry history economics philosophy language art music other
  • Difficulties: intro intermediate advanced
  • coverUrl is null until a cover is uploaded; the site renders a generated cover in that case.

Reads (public)

MethodPathNotes
GET/{courses} newest first with rating: {average, count}, commentCount, myStars when authed
GET/:slugone course, same shape
GET/covers/:courseIdcover bytes, public, immutable cache
GET/:id/commentscomments with author objects

Writes

All writes share PAT bucket courses-write (60/min). Course mutations are owner-scoped (404 for non-owners); ratings and comments are open to any signed-in user.

MethodPathScopeBodyNotes
POST/entries:write{url, title?}Fetches the page server-side and classifies it with an LLM. 502 if the URL is unreachable; classification failure falls back to safe defaults (never blocks). 409 duplicate url. Slug generated from title, stable forever
PATCH/:identries:writeany of title, description, category, difficulty, durationMinutes, tags (max 8), url409 on url clash
POST/:id/reclassifyentries:writenoneRe-runs the AI; overwrites description/category/difficulty/duration/tags but PRESERVES title and slug
DELETE/:identries:writenone204, cascades ratings and comments
PUT/:id/coverentries:writemultipart fileSame image ladder as journal: sniffed png/jpeg/gif/webp, 5MB 413, 415 otherwise
DELETE/:id/coverentries:writenoneback to the generated cover
PUT/:id/ratingreactions:write{stars} int 1..5Upsert per (user, course); returns {rating:{average, count, mine}}
DELETE/:id/ratingreactions:writenoneclears yours, returns fresh aggregate
POST/:id/commentscomments:write{content, parent_comment_id?} 1..10kmarkdown, one reply level
PATCH/comments/:idcomments:write{content}author only
DELETE/comments/:idcomments:writenonecomment author or course owner; 204

Example

curl -X POST https://shanebackend-production.up.railway.app/api/courses \
  -H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
  -d '{"url":"https://supermassive-courses-production.up.railway.app/courses/pi2-heist/"}'
# 201 with AI-filled category/difficulty/duration/tags (allow ~10s)