Skip to content

API Reference

Complete reference for all API endpoints in the 2Sigma Backend.

Base URL

http://localhost:8000/api/v1

Interactive Documentation

Authentication

Most endpoints require JWT authentication using the Bearer token scheme.

Adding Authentication Header

Authorization: Bearer YOUR_ACCESS_TOKEN

Getting Tokens

See Authentication Endpoints below.

API Endpoint Groups

  • Authentication - Registration, login, token refresh
  • Users - User management and profiles
  • Universities - University and department management
  • Courses - Course definitions and offerings
  • Enrollments - Course enrollment management
  • Modules - Hierarchical course content structure
  • Content - Learning materials (videos, quizzes, labs)
  • Content Types - Admin content type management
  • Progress - Module and content progress tracking
  • Chat - AI tutoring chat sessions
  • Prompts - Langfuse-style prompt management
  • Feature Requests - User feedback and feature request tracking
  • Admin - Administrative operations and analytics dashboard

Authentication

Check Email Availability

POST /auth/check-email

Check if an email is available for registration.

Request Body:

{
  "email": "user@example.com"
}

Response:

{
  "available": true
}

Register User

POST /auth/register

Register a new user account.

Request Body:

{
  "email": "user@example.com",
  "password": "SecurePass123",
  "full_name": "John Doe",
  "locale": "en",
  "time_zone": "UTC"
}

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer"
}

Register with Onboarding

POST /auth/register-with-onboarding

Register user with complete profile and preferences.

Request Body:

{
  "email": "user@example.com",
  "password": "SecurePass123",
  "full_name": "John Doe",
  "profile": {
    "date_of_birth": "2000-01-01",
    "learning_goal": "CAREER_ADVANCEMENT",
    "study_level": "UNDERGRADUATE",
    "grade": "SOPHOMORE"
  },
  "subject_preferences": [1, 2, 3]
}

Login

POST /auth/login

Login with email and password.

Request Body:

{
  "email": "user@example.com",
  "password": "SecurePass123"
}

Response: Same as register (tokens)

Refresh Token

POST /auth/refresh

Get new access token using refresh token.

Headers:

Authorization: Bearer YOUR_REFRESH_TOKEN

Response: New access and refresh tokens


Users

Get Current User

GET /users/me

Get authenticated user's profile.

Auth: Required

Response:

{
  "id": 1,
  "email": "user@example.com",
  "full_name": "John Doe",
  "is_active": true,
  "locale": "en",
  "time_zone": "UTC",
  "current_streak_days": 5,
  "last_activity_date": "2026-01-20",
  "created_at": "2026-01-01T00:00:00Z"
}

Get User Overview

GET /users/me/overview

Get dashboard overview with enrollments, recommendations, and stats.

Auth: Required

Response:

{
  "active_enrollments": [...],
  "recommended_courses": [...],
  "recent_activity": [...],
  "stats": {
    "completed_courses": 5,
    "current_streak": 3
  }
}

Update Current User

PUT /users/me

Update authenticated user's profile.

Auth: Required

Request Body:

{
  "full_name": "Jane Doe",
  "locale": "es",
  "time_zone": "America/New_York"
}

Get User by ID

GET /users/{user_id}

Auth: Required

List Users

GET /users/?skip=0&limit=100

Auth: Required


Courses

List Courses

GET /courses/?university_id=1&skip=0&limit=100

List all courses, optionally filtered by university.

Query Parameters: - university_id (optional) - Filter by university - skip (default: 0) - Pagination offset - limit (default: 100) - Results per page

Response:

[
  {
    "id": 1,
    "title": "Introduction to Computer Science",
    "code": "CS101",
    "description": "Fundamentals of programming",
    "level": "INTRODUCTORY",
    "credit_hours": 3,
    "is_published": true,
    "university_id": 1,
    "department_id": 1
  }
]

Get Course

GET /courses/{course_id}

Get detailed course information.

Create Course

POST /courses/

Auth: Required

Request Body:

{
  "title": "Data Structures",
  "code": "CS201",
  "description": "Study of data structures",
  "level": "INTERMEDIATE",
  "credit_hours": 4,
  "university_id": 1,
  "department_id": 1,
  "primary_subject_id": 1,
  "cache_enabled": true
}

Update Course

PUT /courses/{course_id}

Auth: Required

Delete Course

DELETE /courses/{course_id}

Auth: Required

List Course Offerings

GET /courses/{course_id}/offerings

Get all offerings (terms/instances) for a course.

Create Course Offering

POST /courses/{course_id}/offerings

Auth: Required

Request Body:

{
  "term_name": "Spring 2026",
  "starts_at": "2026-01-15T00:00:00Z",
  "ends_at": "2026-05-15T00:00:00Z",
  "enrollment_start": "2025-12-01T00:00:00Z",
  "enrollment_end": "2026-01-31T00:00:00Z",
  "visibility": "PUBLIC"
}

Get Course Statistics

GET /courses/{course_id}/stats

Auth: Required


Enrollments

Get My Enrollments

GET /enrollments/my-enrollments

Get current user's course enrollments.

Auth: Required

Response:

[
  {
    "id": 1,
    "user_id": 1,
    "course_offering_id": 1,
    "status": "ACTIVE",
    "enrolled_at": "2026-01-01T00:00:00Z",
    "course_offering": {
      "term_name": "Spring 2026",
      "course": {
        "title": "CS101",
        "code": "CS101"
      }
    }
  }
]

Enroll in Course

POST /enrollments/

Auth: Required

Request Body:

{
  "course_offering_id": 1
}

Update Enrollment Status

PUT /enrollments/{enrollment_id}

Auth: Required

Request Body:

{
  "status": "COMPLETED"
}


Modules

Modules form a hierarchical tree structure for course content organization.

List Course Modules

GET /modules/courses/{course_id}/modules

Get all modules for a course (flat list).

List Root Modules

GET /modules/courses/{course_id}/modules/root

Get only top-level modules (entry points).

Get Module

GET /modules/{module_id}

Get Child Modules

GET /modules/{module_id}/children

Get direct children of a module.

Create Module

POST /modules/

Auth: Required

Request Body:

{
  "course_id": 1,
  "parent_module_id": null,
  "title": "Week 1: Introduction",
  "description": "Course introduction",
  "module_type": "UNIT",
  "sequence_index": 0
}

Module Types: COURSE_ROOT, UNIT, CHAPTER, LESSON, TOPIC, SECTION, LAB, QUIZ

Reorder Modules

POST /modules/reorder

Auth: Required

Request Body:

{
  "module_ids": [3, 1, 2]
}


Content

Content items are learning materials within modules.

List Module Content

GET /content/modules/{module_id}/content

Get all content items for a module.

Get Content Item

GET /content/{content_id}

Response:

{
  "id": 1,
  "module_id": 1,
  "title": "Introduction Video",
  "content_type_id": 1,
  "content_type": {
    "name": "video",
    "label": "Video"
  },
  "description": "Course intro video",
  "data_json": {
    "video_url": "https://...",
    "duration": 600
  },
  "sequence_index": 0,
  "estimated_duration_seconds": 600
}

Create Content

POST /content/

Auth: Required

Request Body:

{
  "module_id": 1,
  "course_id": 1,
  "content_type_id": 1,
  "title": "Lecture Video",
  "description": "First lecture",
  "data_json": {
    "video_url": "https://example.com/video.mp4"
  },
  "estimated_duration_seconds": 1800,
  "sequence_index": 0
}


Progress

Track user progress through modules and content.

Get My Module Progress

GET /progress/modules/my-progress?course_offering_id=1

Auth: Required

Create/Update Module Progress

POST /progress/modules

Auth: Required

Request Body:

{
  "module_id": 1,
  "course_offering_id": 1,
  "progress_percent": 50,
  "completed_at": null
}

Get My Content Progress

GET /progress/content/my-progress?course_offering_id=1

Auth: Required

Create/Update Content Progress

POST /progress/content

Auth: Required

Request Body:

{
  "content_item_id": 1,
  "course_offering_id": 1,
  "progress_percent": 100,
  "completed_at": "2026-01-20T10:30:00Z"
}


Chat

AI-powered tutoring chat sessions with LLM response caching. When multiple users study the same topic and follow the same conversation path (greeting → MCQ answers), cached responses are served from the database instead of calling the LLM. This is transparent to the client — cached responses use the identical SSE format.

Cache Behavior

The chat system uses a tree-structured response cache keyed by (content_item, prompt_version, model, user_message_sequence):

  • Cache HIT: Response streamed from database using stored SSE chunk boundaries. No LLM call made. Message saved with meta.source = "cache".
  • Cache MISS: LLM called normally. Response chunks captured and stored in cache tree for future users. Message saved with meta.source = "llm".
  • Cacheable messages: "Start." (greeting trigger), MCQ quiz answers ({"questionId": ..., "answer": ...})
  • Uncacheable messages: Free-form text, follow-up messages ({"type": "followup", ...}). These permanently set the session to DIVERGED state — all subsequent messages in that session go to LLM.

Per-Course Cache Toggle

Caching can be disabled per course via the cache_enabled boolean on the Course model (default: true). When disabled, both send_message and generate_greeting skip cache lookup and storage entirely.

Cache Invalidation

Cache automatically misses when: - Prompt version changes (admin publishes new prompt version) - LLM model changes (different model configured for the course) - Cache is disabled for the course (cache_enabled = false) - No manual cache clearing needed — old entries cleaned up by periodic job

List Chat Sessions

GET /chat/sessions

Auth: Required

Create Chat Session

POST /chat/sessions

Auth: Required

Request Body:

{
  "content_item_id": 1
}

Response:

{
  "id": 1,
  "user_id": 1,
  "content_item_id": 1,
  "content_context": {...},
  "share_enabled": false,
  "share_token": null,
  "cache_node_id": null,
  "cache_state": "TRACKING",
  "created_at": "2026-02-25T00:00:00Z",
  "updated_at": "2026-02-25T00:00:00Z"
}

New fields in response: - cache_node_id (integer, nullable) — Current position in the cache tree. null means at root (no cached greeting served yet). - cache_state (string) — "TRACKING" (session following cache tree) or "DIVERGED" (user sent free-form text, cache disabled for this session).

Get Chat Session

GET /chat/sessions/{session_id}

Auth: Required

Send Message (Streaming)

POST /chat/sessions/{session_id}/messages

Auth: Required

Request Body:

{
  "content": "Explain recursion"
}

Response: Server-Sent Events (SSE) stream

SSE Event Format (identical for both cache hits and LLM calls):

data: {"content": "Here is ", "done": false}
data: {"content": "the explanation...", "done": false}
data: {"content": "", "done": true, "message_id": 42}

Cache behavior per message type:

Message Type Example Cacheable Behavior
Greeting trigger "Start." ✅ Yes Root node lookup in cache tree
MCQ answer {"questionId": 1, "answer": "A"} ✅ Yes Child node lookup by parent + hash
Free-form text "Why does this work?" ❌ No LLM call, session DIVERGED permanently
Follow-up {"type": "followup", ...} ❌ No LLM call, session DIVERGED permanently

Message metadata (chat_messages.meta): - "source": "llm" — response generated by LLM (cache miss or diverged session) - "source": "cache" — response served from cache - "cache_node_id": 123 — which cache tree node was used (present on cache hits)

Generate Greeting

POST /chat/sessions/{session_id}/greeting

Auth: Required

Generate AI greeting for new session. Internally sends "Start." as the user message.

Cache behavior: The greeting is always the root node of the cache tree. First user for a given (topic, prompt version, model) populates the cache; subsequent users get the cached greeting instantly.

Share Session

POST /chat/sessions/{session_id}/share

Auth: Required

Response:

{
  "share_token": "abc123def456",
  "share_url": "/api/v1/chat/share/abc123def456"
}

Get Shared Session (Public)

GET /chat/share/{share_token}

Auth: Not required


Prompts

Langfuse-style versioned prompt management with label-based version resolution (admin feature).

List Prompts

GET /prompts/?name=tutor&label=production&tag=math&page=1&page_size=20

Auth: Required (INSTRUCTOR+)

Query Parameters: - name (optional) - Filter by name (ILIKE) - label (optional) - Filter by label - tag (optional) - Filter by tag - page (default: 1) - page_size (default: 20)

Response: Prompts grouped by name with aggregated versions, labels, and tags.

{
  "data": [
    {
      "name": "General Protocol",
      "type": "text",
      "versions": [1, 2, 3],
      "labels": ["production", "latest"],
      "tags": ["core", "protocol"],
      "last_updated_at": "2026-02-06T10:00:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 20
}

Get Prompt by Name

GET /prompts/{prompt_name}?label=production

Auth: Required (INSTRUCTOR+)

Query Parameters: - version (optional int) - Resolve specific version - label (optional str) - Resolve by label (default: production) - Cannot specify both version and label

Response: Includes resolved prompt text with all dependencies inlined.

{
  "id": 5,
  "name": "Video Tutor",
  "version": 3,
  "type": "text",
  "prompt": "You are a helpful tutor for {{title}}...\n@@@langfusePrompt:name=General Protocol|label=production@@@",
  "config": {},
  "labels": ["production"],
  "tags": ["tutor", "video"],
  "commit_message": "Added protocol reference",
  "created_by": "42",
  "created_at": "2026-02-06T10:00:00Z",
  "updated_at": "2026-02-06T10:00:00Z",
  "dependencies": [
    {"child_name": "General Protocol", "child_version": null, "child_label": "production"}
  ],
  "resolved_prompt": "You are a helpful tutor for {{title}}...\nFollow these rules: ...",
  "resolution_graph": {"root": {"name": "Video Tutor", "version": 3, "id": 5}, "dependencies": {}}
}

Get All Versions

GET /prompts/{prompt_name}/versions?page=1&page_size=20

Auth: Required (INSTRUCTOR+)

Response:

{
  "versions": [
    {
      "id": 5,
      "name": "Video Tutor",
      "version": 3,
      "labels": ["production", "latest"],
      "commit_message": "Added protocol reference",
      "created_by": "42",
      "creator": "Jane Doe",
      "created_at": "2026-02-06T10:00:00Z"
    }
  ],
  "total": 3
}

Create Prompt

POST /prompts/

Auth: Required (INSTRUCTOR+)

Request Body:

{
  "name": "Video Tutor",
  "prompt": "You are a helpful tutor for {{title}}...",
  "config": {},
  "labels": ["production"],
  "tags": ["tutor", "video"],
  "commit_message": "Initial version"
}

Response: 201 Created with PromptResponse

Notes: - Version auto-increments per prompt name - latest label is auto-assigned to new versions - Dependencies are parsed from @@@langfusePrompt:...@@@ tags in prompt text - Tags are synchronized across all versions of a prompt name

Set Labels

PATCH /prompts/{prompt_name}/versions/{version}/labels

Auth: Required (INSTRUCTOR+; PLATFORM_ADMIN for protected labels)

Request Body:

{
  "labels": ["production", "staging"]
}

Notes: - latest cannot be manually set (auto-managed) - Labels are unique per prompt name — setting a label on one version removes it from others

Update Tags

PATCH /prompts/{prompt_name}/tags

Auth: Required (INSTRUCTOR+)

Request Body:

{
  "tags": ["core", "protocol", "v2"]
}

Response: {"success": true}

Note: Updates tags on ALL versions of the prompt name.

Delete Prompt (All Versions)

DELETE /prompts/{prompt_name}

Auth: Required (PLATFORM_ADMIN only)

Response: 204 No Content

Note: Blocked if other prompts depend on any version/label of this prompt.

Delete Prompt Version

DELETE /prompts/{prompt_name}/versions/{version}

Auth: Required (PLATFORM_ADMIN only)

Response: 204 No Content

Note: If the deleted version had the latest label, it is reassigned to the next highest version.

Get All Labels

GET /prompts/labels

Auth: Required (INSTRUCTOR+)

Response: ["production", "latest", "staging", "experiment-a"]

Get All Names

GET /prompts/names

Auth: Required (INSTRUCTOR+)

Response: [{"name": "General Protocol", "id": 1}, {"name": "Video Tutor", "id": 5}]

GET /prompts/link-options

Auth: Required (INSTRUCTOR+)

Response: Used by the prompt reference insertion dialog.

[
  {
    "name": "General Protocol",
    "versions": [1, 2, 3],
    "labels": ["production", "latest"]
  }
]

Preview Prompt

POST /prompts/preview

Auth: Required (INSTRUCTOR+)

Request Body:

{
  "prompt_text": "Hello {{title}}, @@@langfusePrompt:name=General Protocol|label=production@@@",
  "variables": {"title": "Math 101"}
}

Response:

{
  "rendered_prompt": "Hello Math 101, Follow these rules: ...",
  "variables_used": ["title"],
  "dependencies_found": [
    {"child_name": "General Protocol", "child_version": null, "child_label": "production"}
  ]
}


Feature Requests

User-submitted feature requests with admin management.

Submit Feature Request

POST /feature-requests/

Auth: Required

Request Body:

{
  "title": "Dark mode support",
  "description": "Add a dark mode toggle to the settings page for better readability at night."
}

Response: 201 Created with the created feature request object including id, status (default: pending), and timestamps.

List My Feature Requests

GET /feature-requests/

Auth: Required

Query Parameters:

Parameter Type Description
skip integer Offset (default: 0)
limit integer Page size (default: 20, max: 100)

Response: Paginated list of the current user's feature requests with data array and total count.

List All Feature Requests (Admin)

GET /feature-requests/all

Auth: Required (instructor or above)

Query Parameters:

Parameter Type Description
skip integer Offset (default: 0)
limit integer Page size (default: 20, max: 100)
status string Filter by status: pending, under_review, planned, completed, declined

Response: Paginated list with user info (user_email, user_name) on each request.

Update Feature Request Status (Admin)

PATCH /feature-requests/{feature_request_id}

Auth: Required (instructor or above)

Request Body:

{
  "status": "under_review",
  "admin_notes": "Discussing with the design team."
}

Both fields are optional. Valid status values: pending, under_review, planned, completed, declined.


Admin

Administrative operations.

Update AWS Credentials

POST /admin/aws-credentials

Auth: Required (admin)

Request Body:

{
  "aws_access_key_id": "AKIAIOSFODNN7EXAMPLE",
  "aws_secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  "aws_session_token": "optional"
}

Credentials are encrypted and stored in database.

Check AWS Credentials Status

GET /admin/aws-credentials/status

Auth: Required (admin)

Test AWS Bedrock Connection

POST /admin/aws-credentials/test

Auth: Required (admin)

Analytics — Overview

GET /admin/analytics/overview

Auth: Required (platform admin)

Query Parameters:

Parameter Type Description
date_from date Filter start date (inclusive)
date_to date Filter end date (inclusive)

Response: Total registered users, daily new signups, active user counts (daily/weekly/monthly), and streak distribution.

Analytics — Engagement

GET /admin/analytics/engagement

Auth: Required (platform admin)

Query Parameters:

Parameter Type Description
date_from date Filter start date (inclusive)
date_to date Filter end date (inclusive)
course_id integer Filter by course

Response: Average time per topic, average chat turns per topic per user, average session duration, content started/completed counts, and completion rate.

Analytics — Progress

GET /admin/analytics/progress

Auth: Required (platform admin)

Query Parameters:

Parameter Type Description
date_from date Filter start date (inclusive)
date_to date Filter end date (inclusive)
course_id integer Filter by course

Response: Average mastered topics per user, course tree completion/interruption counts, enrollment breakdown by status, drop-off count, and per-course breakdown.

Analytics — Assessments

GET /admin/analytics/assessments

Auth: Required (platform admin)

Query Parameters:

Parameter Type Description
date_from date Filter start date (inclusive)
date_to date Filter end date (inclusive)

Response: Quiz attempt counts, pass/fail rates, average quiz score, lab submission counts, and average lab score.

Analytics — AI & Cache

GET /admin/analytics/cache

Auth: Required (platform admin)

Response: LLM cache hit/miss counts, hit rate, session divergence rate, total cached nodes, and total hits served.

Analytics — Community

GET /admin/analytics/community

Auth: Required (platform admin)

Response: Total notes, flashcards, flashcard reviews, discussion threads, discussion posts, average course rating, and total reviews.


Error Responses

All endpoints follow consistent error format:

{
  "detail": "Error message"
}

Status Codes: - 200 - Success - 201 - Created - 204 - No Content (successful deletion) - 400 - Bad Request (invalid data) - 401 - Unauthorized (missing/invalid token) - 403 - Forbidden (insufficient permissions) - 404 - Not Found - 422 - Validation Error - 500 - Internal Server Error

Validation Error Example:

{
  "detail": [
    {
      "loc": ["body", "email"],
      "msg": "value is not a valid email address",
      "type": "value_error.email"
    }
  ]
}

Next Steps