Skip to content

Latest commit

 

History

History
502 lines (386 loc) · 11.6 KB

API.md

File metadata and controls

502 lines (386 loc) · 11.6 KB

API Reference

Base URL: http://localhost:4000/api/v1 (dev)

Interactive docs: http://localhost:4000/api/docs (Swagger UI)

Frontend integration guide: FRONTEND_INTEGRATION.md — full E2E flow from bootstrap through course completion.

All user-facing endpoints require Clerk JWT unless noted.


Authentication

Authorization: Bearer <clerk-session-jwt>

Backend verifies via @clerk/backend verifyToken(). Resolves clerkUserId → profileId via shared Profile table.


Curriculum (agentic bootstrap)

Generate curriculum from onboarding data

Reads Server.learningGoal, learningReason, and LearningMaterial[] from the shared DB. Requires both goal and materials. Writes to shared Curriculum/Module/Quiz tables and backend Concept bank + ServerLearningMeta.

POST /servers/:serverId/bootstrap
Authorization: Bearer <clerk-jwt>

Response 202 Accepted (default async)

{
  "jobId": "uuid",
  "status": "generating"
}

Response 200 when BOOTSTRAP_SYNC=true on the backend

{
  "curriculumId": "clx...",
  "moduleCount": 4,
  "conceptCount": 9,
  "goalCriteria": ["...", "..."],
  "summary": "Personalized path for ..."
}
Code Meaning
400 Missing goal or materials
403 Not server admin
409 Already GENERATING or already READY

Get curriculum status (poll)

Lightweight lifecycle poll. Use while bootstrap runs; does not return lesson content.

GET /servers/:serverId/curriculum
Authorization: Bearer <clerk-jwt>

Response 200

{
  "serverId": "clx...",
  "status": "READY",
  "summary": "4-module path covering ...",
  "modules": [
    { "id": "clx...", "order": 1, "title": "Foundations", "progressStatus": "AVAILABLE" }
  ],
  "conceptCount": 9,
  "goalCriteria": ["Learner can ...", "..."]
}

Curriculum status enum: PENDING | GENERATING | READY | FAILED

Module progressStatus enum: LOCKED | AVAILABLE | IN_PROGRESS | COMPLETED


Get full curriculum content

Returns nested modules, concepts, gate quizzes, and final exam after bootstrap completes.

GET /servers/:serverId/curriculum/content
Authorization: Bearer <clerk-jwt>

Response 200 when status === READY — see Swagger CurriculumContentResponseDto for full shape.

Response 409 Conflict when not ready

{
  "status": "GENERATING",
  "message": "Curriculum not ready yet"
}
status message
PENDING / GENERATING Curriculum not ready yet
FAILED Bootstrap failed; retry POST /bootstrap

Answer redaction: Non-admin members receive learner-safe payloads (no correct flags, solutions, or rubrics). Server admins receive full content.


Conversations

Process message (trigger learning workflow)

Called by the frontend after persisting a user message. Returns immediately; agent replies are pushed via internal webhook.

POST /conversations/:conversationId/process
Authorization: Bearer <clerk-jwt>
Content-Type: application/json

{
  "serverId": "clx...",
  "memberId": "clx...",
  "profileId": "clx...",
  "message": {
    "id": "clx...",
    "content": "Let's start",
    "type": "TEXT"
  },
  "conversationType": "AGENT"
}

Response 202 Accepted

{ "jobId": "uuid", "status": "processing" }

Other statuses (still 202):

status Meaning
processing Workflow enqueued
curriculum_not_ready Bootstrap not finished — no workflow runs
duplicate_ignored Same conversationId called within 5 seconds

Rate limit: 10 requests/minute per client.

Code Meaning
401 Invalid/missing JWT
403 Not a member of server
422 Invalid payload

Note: conversationId is used as the workflow session key. The backend does not validate it against the frontend DB.


Get conversation session state

Returns current workflow step and optional nextAction for UI chips/buttons.

GET /conversations/:conversationId/session?serverId=clx...
Authorization: Bearer <clerk-jwt>

Response 200

{
  "conversationId": "clx...",
  "serverId": "clx...",
  "currentConceptId": "clx...",
  "currentConceptTitle": "Solving linear equations",
  "step": "QUICK_CHECK",
  "nextAction": {
    "type": "MCQ",
    "questionId": "clx...",
    "prompt": "What is the first step when solving 2x + 4 = 10?"
  },
  "progress": {
    "conceptsCompleted": 2,
    "conceptsTotal": 12
  }
}

Workflow step enum: STUDY | QUICK_CHECK | PRACTICE | EXPLAIN_BACK | MICRO_DRILL | CONCEPT_COMPLETE | MODULE_GATE | GOAL_VERIFICATION | MOCK_INTERVIEW | INTERVIEW_DEBRIEF | COURSE_COMPLETE | FREE_CHAT


Assessments

Both endpoints resume the Mastra workflow synchronously. On success, agent messages are pushed to the frontend via the internal webhook (same as /process).

Submit MCQ or practice answer

POST /assessments/submit
Authorization: Bearer <clerk-jwt>
Content-Type: application/json

{
  "conversationId": "clx...",
  "serverId": "clx...",
  "memberId": "clx...",
  "questionId": "clx...",
  "questionType": "COMPREHENSION",
  "answer": "B"
}
questionType answer format
COMPREHENSION MCQ choice ID (e.g. "A", "B")
PRACTICE Free-text solution

Response 200

{
  "status": "processed",
  "messageCount": 3
}

Follow-up agent/system messages arrive via webhook.


Submit explain-back answer

POST /assessments/explain-back
Authorization: Bearer <clerk-jwt>
Content-Type: application/json

{
  "conversationId": "clx...",
  "serverId": "clx...",
  "memberId": "clx...",
  "conceptId": "clx...",
  "answer": "You subtract 4 from both sides first because..."
}

Response 200

{
  "passed": true,
  "feedback": "Good — you identified the isolation step correctly.",
  "scoreDelta": 15,
  "nextStep": "CONCEPT_COMPLETE"
}

On pass, agent messages are also webhook-delivered. On fail, only synchronous feedback is returned; learner returns to STUDY.


Progress

Get learner progress for a course

GET /progress/:serverId
Authorization: Bearer <clerk-jwt>

Response 200

{
  "serverId": "clx...",
  "courseScore": 142,
  "rank": "INTERMEDIATE",
  "concepts": [
    {
      "id": "clx...",
      "title": "Linear equations",
      "mastery": 0.85,
      "status": "COMPLETED",
      "nextReviewAt": "2026-07-20T00:00:00.000Z"
    }
  ],
  "modules": [
    {
      "id": "clx...",
      "title": "Foundations",
      "order": 1,
      "status": "IN_PROGRESS",
      "score": null
    }
  ],
  "currentModuleId": "clx...",
  "goalMet": null,
  "certificateStage": null,
  "interviewStatus": "not_applicable",
  "weakTopics": ["LINEAR_ADD_BEFORE_ISOLATE"],
  "strongTopics": ["isolation-basic"]
}
Field Notes
rank BEGINNER | INTERMEDIATE | ADVANCED | EXPERT
goalMet null until COURSE_COMPLETE step
certificateStage provisional | final | none
interviewStatus not_applicable | not_started | in_progress | passed | failed

Courses (content bank — read only)

List concepts for a course

Flat ordered list — no study units or quiz content. Use webhook messages or GET /curriculum/content for full lesson bodies.

GET /courses/:serverId/concepts
Authorization: Bearer <clerk-jwt>

Response 200

{
  "serverId": "clx...",
  "subject": "Algebra I",
  "agentHandle": "@AlgebraI",
  "concepts": [
    { "id": "clx...", "slug": "linear-equations", "title": "Linear equations", "order": 1 }
  ]
}

Health

GET /health

No auth required.

Response 200

{
  "status": "ok",
  "database": "connected",
  "mastra": "ready"
}

status is degraded when the database is unreachable.


Internal — Backend → Frontend

Implemented on the frontend (not this backend). Documented here for contract alignment. See FRONTEND_INTEGRATION.md § Phase 5.

Agent response callback

POST {CYCY_URL}/api/internal/agent-response
X-Internal-Secret: <CYCY_INTERNAL_SECRET>
Content-Type: application/json

{
  "conversationId": "clx...",
  "messages": [
    {
      "authorType": "AI_AGENT",
      "content": "Let's start with linear equations...",
      "metadata": {
        "type": "EXPLANATION",
        "conceptId": "clx...",
        "step": "STUDY"
      }
    },
    {
      "authorType": "SYSTEM",
      "content": "Quick check",
      "metadata": {
        "type": "MCQ",
        "questionId": "clx...",
        "choices": [
          { "id": "A", "text": "Add 4 to both sides" },
          { "id": "B", "text": "Subtract 4 from both sides" }
        ],
        "step": "QUICK_CHECK"
      }
    }
  ]
}

Expected response 201

{ "saved": 2, "emitted": true }

The frontend must persist each message and emit its real-time channel (e.g. Socket.io chat:{conversationId}:messages).

Message author types

authorType UI treatment
USER Standard user bubble
AI_AGENT Agent avatar + course agent name
SYSTEM Structured UI by metadata.type

Metadata types

metadata.type Frontend component
GREETING Welcome bubble
EXPLANATION Markdown study content
STEP_CHIP Study → Quick check → Practice chips
MCQ Inline multiple-choice buttons
PRACTICE Problem prompt + text input
EXPLAIN_BACK Explain-your-reasoning prompt
GAP_CALLOUT Misconception highlight card
FEEDBACK Inline grading feedback
MOTIVATION Encouragement + XP
CONCEPT_COMPLETE Concept completion celebration
MODULE_COMPLETE Module done banner
MODULE_GATE Gate quiz prompt
GOAL_VERIFICATION Goal pass/fail summary
CERTIFICATE Certificate + verification code
MOCK_INTERVIEW Interview question
INTERVIEW_DEBRIEF Interview result
COURSE_COMPLETE Course finished banner
FREE_CHAT Off-loop tutor answer
ERROR Error banner
PROGRESS Progress update

MCQ choices in webhook payloads never include correct: true — grading is server-side only.


Environment variables (backend)

Variable Purpose
DATABASE_URL Shared PostgreSQL with frontend
CLERK_SECRET_KEY JWT verification
OPENAI_API_KEY LLM (OpenRouter keys sk-or-* auto-route to OpenRouter)
OPENAI_BASE_URL Optional override (default: OpenAI or OpenRouter auto-detect)
OPENAI_MODEL Optional model ID
CYCY_URL Webhook target base URL
CYCY_INTERNAL_SECRET Webhook auth header
BOOTSTRAP_SYNC true = synchronous bootstrap response
PUBLIC_URL / RENDER_EXTERNAL_URL Swagger server URL in production

Error format

{
  "statusCode": 422,
  "message": "Validation failed",
  "errors": [{ "field": "answer", "message": "Required" }]
}

Endpoint index

Method Path Auth
GET /health None
POST /servers/:serverId/bootstrap Clerk
GET /servers/:serverId/curriculum Clerk
GET /servers/:serverId/curriculum/content Clerk
POST /conversations/:conversationId/process Clerk
GET /conversations/:conversationId/session Clerk
POST /assessments/submit Clerk
POST /assessments/explain-back Clerk
GET /progress/:serverId Clerk
GET /courses/:serverId/concepts Clerk