{"openapi":"3.0.3","info":{"title":"pass-life-in-uk API","version":"0.1.1","description":"Generic backend starter API with production-ready platform modules."},"servers":[{"url":"/"}],"tags":[{"name":"System","description":"Operational and metadata endpoints"},{"name":"Documentation","description":"API contract and interactive documentation endpoints"},{"name":"Discovery","description":"Search and AI crawler discovery endpoints"},{"name":"Auth","description":"OAuth login, token refresh, and session endpoints"},{"name":"Study","description":"Lessons, questions, and answer recording endpoints"},{"name":"Review","description":"Adaptive review card generation endpoints"},{"name":"Mock","description":"Mock test generation, scoring, and history endpoints"},{"name":"Dashboard","description":"Learner analytics and readiness summary endpoints"},{"name":"Onboarding","description":"User onboarding preferences and study setup endpoints"},{"name":"Billing","description":"Stripe checkout, portal, and webhook endpoints"}],"components":{"schemas":{},"parameters":{}},"paths":{"/api/v1/health":{"get":{"tags":["System"],"summary":"Liveness probe","responses":{"200":{"description":"Service is alive","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"uptimeSeconds":{"type":"number","minimum":0},"timestamp":{"type":"string","format":"date-time"}},"required":["status","uptimeSeconds","timestamp"]},"requestId":{"type":"string","minLength":1}},"required":["data","requestId"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"details":{"nullable":true}},"required":["code","message"]},"requestId":{"type":"string","minLength":1}},"required":["error","requestId"]}}}}}}},"/api/v1/ready":{"get":{"tags":["System"],"summary":"Readiness probe","responses":{"200":{"description":"Service is ready","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["ready","not_ready"]},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1},"status":{"type":"string","enum":["ok","fail"]},"message":{"type":"string","minLength":1}},"required":["name","status"]}},"timestamp":{"type":"string","format":"date-time"}},"required":["status","checks","timestamp"]},"requestId":{"type":"string","minLength":1}},"required":["data","requestId"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"details":{"nullable":true}},"required":["code","message"]},"requestId":{"type":"string","minLength":1}},"required":["error","requestId"]}}}},"503":{"description":"Service is not ready","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["ready","not_ready"]},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1},"status":{"type":"string","enum":["ok","fail"]},"message":{"type":"string","minLength":1}},"required":["name","status"]}},"timestamp":{"type":"string","format":"date-time"}},"required":["status","checks","timestamp"]},"requestId":{"type":"string","minLength":1}},"required":["data","requestId"]}}}}}}},"/api/v1/meta":{"get":{"tags":["System"],"summary":"Runtime metadata","responses":{"200":{"description":"Service metadata","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"service":{"type":"string","minLength":1},"version":{"type":"string","minLength":1},"environment":{"type":"string","enum":["local","preview","production"]},"commitSha":{"type":"string","nullable":true}},"required":["service","version","environment","commitSha"]},"requestId":{"type":"string","minLength":1}},"required":["data","requestId"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"details":{"nullable":true}},"required":["code","message"]},"requestId":{"type":"string","minLength":1}},"required":["error","requestId"]}}}}}}},"/api/v1/openapi.json":{"get":{"tags":["Documentation"],"summary":"OpenAPI specification document","responses":{"200":{"description":"OpenAPI specification","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"nullable":true}}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"details":{"nullable":true}},"required":["code","message"]},"requestId":{"type":"string","minLength":1}},"required":["error","requestId"]}}}}}}},"/api/v1/docs":{"get":{"tags":["Documentation"],"summary":"Swagger UI documentation","responses":{"301":{"description":"Redirects to the Swagger UI page at /api/v1/docs/"},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"details":{"nullable":true}},"required":["code","message"]},"requestId":{"type":"string","minLength":1}},"required":["error","requestId"]}}}}}}},"/robots.txt":{"get":{"tags":["Discovery"],"summary":"Robots directives for crawlers","responses":{"200":{"description":"robots.txt directives","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"details":{"nullable":true}},"required":["code","message"]},"requestId":{"type":"string","minLength":1}},"required":["error","requestId"]}}}}}}},"/sitemap.xml":{"get":{"tags":["Discovery"],"summary":"Sitemap for search engine discovery","responses":{"200":{"description":"XML sitemap","content":{"application/xml":{"schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"details":{"nullable":true}},"required":["code","message"]},"requestId":{"type":"string","minLength":1}},"required":["error","requestId"]}}}}}}},"/llms.txt":{"get":{"tags":["Discovery"],"summary":"LLM-oriented project summary and canonical links","responses":{"200":{"description":"LLM discovery document","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1},"details":{"nullable":true}},"required":["code","message"]},"requestId":{"type":"string","minLength":1}},"required":["error","requestId"]}}}}}}},"/api/v1/auth/google/start":{"get":{"tags":["Auth"],"summary":"Start Google OAuth sign-in","responses":{"302":{"description":"302 response"},"400":{"description":"400 response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"503 response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/auth/google/callback":{"get":{"tags":["Auth"],"summary":"Handle Google OAuth callback and redirect to frontend","responses":{"302":{"description":"302 response"},"400":{"description":"400 response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"503 response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/auth/session":{"get":{"tags":["Auth"],"summary":"Resolve current auth session from bearer token","responses":{"200":{"description":"Current auth session","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"authenticated":{"type":"boolean","enum":[true]},"user":{"type":"object","properties":{"id":{"type":"string","minLength":1},"email":{"type":"string","nullable":true,"format":"email"},"name":{"type":"string","nullable":true,"minLength":1},"image":{"type":"string","nullable":true,"format":"uri"}},"required":["id"]}},"required":["authenticated","user"]},{"type":"object","properties":{"authenticated":{"type":"boolean","enum":[false]}},"required":["authenticated"]}]}}}},"401":{"description":"Invalid access token","content":{"application/json":{"schema":{"type":"object","properties":{"authenticated":{"type":"boolean","enum":[false]},"error":{"type":"string","minLength":1}},"required":["authenticated","error"]}}}},"503":{"description":"Auth is not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/auth/refresh":{"post":{"tags":["Auth"],"summary":"Refresh access and refresh tokens","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"refreshToken":{"type":"string","minLength":10}},"required":["refreshToken"]}}}},"responses":{"200":{"description":"Refreshed access and refresh tokens","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string","minLength":1},"refreshToken":{"type":"string","minLength":1}},"required":["accessToken","refreshToken"]}}}},"400":{"description":"Invalid refresh request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Refresh token is invalid","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Auth is not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/auth/logout/handoff":{"post":{"tags":["Auth"],"summary":"Persist signed-in limit state onto the current device guest identity before logout","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"guestId":{"type":"string","minLength":8}},"required":["guestId"]}}}},"responses":{"200":{"description":"Logout handoff completed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"description":"Invalid logout handoff request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Logout handoff unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["Auth"],"summary":"Revoke refresh token and log out","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"refreshToken":{"type":"string","minLength":10}}}}}},"responses":{"200":{"description":"Logout completed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"description":"Invalid logout request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/learner-state/sync":{"post":{"tags":["Auth"],"summary":"Reconcile learner local state with the authenticated server state","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["bootstrap","write-through"]},"guestId":{"type":"string","minLength":8},"state":{"type":"object","properties":{"currentLessonIndex":{"type":"integer","minimum":0},"onboardingDraft":{"type":"object","nullable":true,"properties":{"reason":{"type":"string","minLength":1},"goal":{"type":"string","minLength":1},"start":{"type":"string","minLength":1},"step":{"type":"integer","minimum":0}},"additionalProperties":false},"onboarding":{"type":"object","nullable":true,"properties":{"reason":{"type":"string","minLength":1},"goal":{"type":"string","minLength":1},"start":{"type":"string","minLength":1}},"required":["reason","goal","start"]},"mockDraft":{"type":"object","nullable":true,"properties":{"answers":{"type":"object","additionalProperties":{"type":"array","items":{"type":"integer","minimum":0}}},"state":{"type":"object","properties":{"currentIndex":{"type":"integer","minimum":0},"timeLeft":{"type":"integer","minimum":0},"lastSavedAt":{"type":"integer","minimum":0}}}},"required":["answers","state"]},"clientProgress":{"type":"object","nullable":true,"properties":{"activeDays":{"type":"integer","minimum":0},"lastActiveAt":{"type":"string","minLength":1},"lessonsCompleted":{"type":"integer","minimum":0},"questionsAnswered":{"type":"integer","minimum":0},"correctAnswers":{"type":"integer","minimum":0},"lastPromptedAt":{"type":"string","minLength":1}},"required":["activeDays","lastActiveAt","lessonsCompleted","questionsAnswered","correctAnswers"]}},"required":["currentLessonIndex","onboardingDraft","onboarding","mockDraft","clientProgress"]}},"required":["mode","state"]}}}},"responses":{"200":{"description":"Canonical learner state after reconciliation","content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"object","properties":{"currentLessonIndex":{"type":"integer","minimum":0},"onboardingDraft":{"type":"object","nullable":true,"properties":{"reason":{"type":"string","minLength":1},"goal":{"type":"string","minLength":1},"start":{"type":"string","minLength":1},"step":{"type":"integer","minimum":0}},"additionalProperties":false},"onboarding":{"type":"object","nullable":true,"properties":{"reason":{"type":"string","minLength":1},"goal":{"type":"string","minLength":1},"start":{"type":"string","minLength":1}},"required":["reason","goal","start"]},"mockDraft":{"type":"object","nullable":true,"properties":{"answers":{"type":"object","additionalProperties":{"type":"array","items":{"type":"integer","minimum":0}}},"state":{"type":"object","properties":{"currentIndex":{"type":"integer","minimum":0},"timeLeft":{"type":"integer","minimum":0},"lastSavedAt":{"type":"integer","minimum":0}}}},"required":["answers","state"]},"clientProgress":{"type":"object","nullable":true,"properties":{"activeDays":{"type":"integer","minimum":0},"lastActiveAt":{"type":"string","minLength":1},"lessonsCompleted":{"type":"integer","minimum":0},"questionsAnswered":{"type":"integer","minimum":0},"correctAnswers":{"type":"integer","minimum":0},"lastPromptedAt":{"type":"string","minLength":1}},"required":["activeDays","lastActiveAt","lessonsCompleted","questionsAnswered","correctAnswers"]}},"required":["currentLessonIndex","onboardingDraft","onboarding","mockDraft","clientProgress"]},"mergedGuestAnswers":{"type":"integer","minimum":0},"mergedGuestMocks":{"type":"integer","minimum":0}},"required":["state","mergedGuestAnswers","mergedGuestMocks"]}}}},"400":{"description":"Invalid learner state payload","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Learner state service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/questions":{"get":{"tags":["Study"],"summary":"Fetch practice questions","parameters":[{"schema":{"type":"integer","minimum":1},"required":false,"name":"chapter","in":"query"},{"schema":{"type":"string","minLength":8},"required":false,"name":"guestId","in":"query"},{"schema":{"type":"boolean"},"required":false,"name":"includeMulti","in":"query"},{"schema":{"type":"boolean"},"required":false,"name":"random","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Practice questions","content":{"application/json":{"schema":{"type":"object","properties":{"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"chapterIndex":{"type":"integer","minimum":1},"chapter":{"type":"string","minLength":1},"prompt":{"type":"string","minLength":1},"choices":{"type":"array","items":{"type":"string","minLength":1},"minItems":2},"requiredSelections":{"type":"integer","minimum":1}},"required":["id","chapterIndex","chapter","prompt","choices","requiredSelections"]}}},"required":["questions"]}}}},"401":{"description":"Guest id or auth is required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Question service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/lessons":{"get":{"tags":["Study"],"summary":"Fetch study lessons","parameters":[{"schema":{"type":"integer","minimum":1},"required":false,"name":"chapter","in":"query"},{"schema":{"type":"string","minLength":8},"required":false,"name":"guestId","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","minimum":1},"required":false,"name":"page","in":"query"}],"responses":{"200":{"description":"Study lessons or plan limit response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"lessons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"chapterIndex":{"type":"integer","minimum":1},"chapter":{"type":"string","minLength":1},"title":{"type":"string","minLength":1},"summary":{"type":"string","minLength":1},"bullets":{"type":"array","items":{"type":"string","minLength":1},"minItems":1},"order":{"type":"integer","minimum":1}},"required":["id","chapterIndex","chapter","title","summary","bullets","order"]}},"total":{"type":"integer","minimum":0},"page":{"type":"integer","minimum":1},"limit":{"type":"integer","minimum":1},"serverLessonIndex":{"type":"integer","minimum":0},"degraded":{"type":"boolean"}},"required":["lessons","total","page","limit"]},{"type":"object","properties":{"limited":{"type":"boolean","enum":[true]},"remaining":{"type":"integer","minimum":0},"upgrade":{"type":"string","minLength":1}},"required":["limited","remaining","upgrade"]}]}}}},"401":{"description":"Guest id or auth is required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"500":{"description":"Progress could not be loaded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Lesson service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}},"post":{"tags":["Study"],"summary":"Persist current lesson progress","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"currentLessonIndex":{"type":"integer","minimum":0}},"required":["currentLessonIndex"]}}}},"responses":{"200":{"description":"Lesson progress saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"description":"Invalid lesson progress payload","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Lesson service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/answers":{"post":{"tags":["Study"],"summary":"Record a study, review, or mock answer","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"questionId":{"type":"string","minLength":1},"chosenIndex":{"type":"integer","minimum":0},"chosenIndexes":{"type":"array","items":{"type":"integer","minimum":0}},"correct":{"type":"boolean"},"chapter":{"type":"string","minLength":1},"chapterIndex":{"type":"integer","minimum":1},"source":{"type":"string","enum":["study","review","mock"]},"guestId":{"type":"string","minLength":8}},"required":["questionId"]}}}},"responses":{"200":{"description":"Recorded answer feedback","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"questionId":{"type":"string","minLength":1},"correct":{"type":"boolean"},"correctIndexes":{"type":"array","items":{"type":"integer","minimum":0}},"explanation":{"type":"string","minLength":1},"chapter":{"type":"string","minLength":1},"degraded":{"type":"boolean"}},"required":["ok","questionId","correct","correctIndexes","explanation","chapter"]}}}},"400":{"description":"Invalid answer payload","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication or guest id required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Answer persistence unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}},"get":{"tags":["Study"],"summary":"Fetch historical answers for the signed-in learner","responses":{"200":{"description":"Historical learner answers","content":{"application/json":{"schema":{"type":"object","properties":{"answers":{"type":"array","items":{"type":"object","properties":{"questionId":{"type":"string","minLength":1},"correct":{"type":"boolean"},"chapter":{"type":"string","minLength":1},"source":{"type":"string","enum":["study","review","mock"]},"answeredAt":{"type":"string","minLength":1}},"required":["questionId"]}}},"required":["answers"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Answer history unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}},"patch":{"tags":["Study"],"summary":"Merge guest answers into the authenticated account","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"guestId":{"type":"string","minLength":8},"currentLessonIndex":{"type":"integer","minimum":0}},"required":["guestId"]}}}},"responses":{"200":{"description":"Guest answers merged","content":{"application/json":{"schema":{"type":"object","properties":{"merged":{"type":"integer","minimum":0}},"required":["merged"]}}}},"400":{"description":"Invalid merge payload","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Merge unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/review":{"get":{"tags":["Review"],"summary":"Generate smart review cards","parameters":[{"schema":{"type":"string","minLength":8},"required":false,"name":"guestId","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Review cards or plan limit response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"cards":{"type":"array","items":{"type":"object","properties":{"questionId":{"type":"string","minLength":1},"prompt":{"type":"string","minLength":1},"choices":{"type":"array","items":{"type":"string","minLength":1},"minItems":2},"chapter":{"type":"string","minLength":1},"reason":{"type":"string","minLength":1}},"required":["questionId","prompt","choices","chapter","reason"]}},"focusChapters":{"type":"array","items":{"type":"string","minLength":1}},"degraded":{"type":"boolean"}},"required":["cards"]},{"type":"object","properties":{"limited":{"type":"boolean","enum":[true]},"remaining":{"type":"integer","minimum":0},"upgrade":{"type":"string","minLength":1},"cards":{"type":"array","items":{"type":"object","properties":{"questionId":{"type":"string","minLength":1},"prompt":{"type":"string","minLength":1},"choices":{"type":"array","items":{"type":"string","minLength":1},"minItems":2},"chapter":{"type":"string","minLength":1},"reason":{"type":"string","minLength":1}},"required":["questionId","prompt","choices","chapter","reason"]},"default":[]},"focusChapters":{"type":"array","items":{"type":"string","minLength":1},"default":[]}},"required":["limited","remaining","upgrade"]}]}}}},"401":{"description":"Authentication or guest id required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Review generation unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/mock":{"get":{"tags":["Mock"],"summary":"Start a mock test","parameters":[{"schema":{"type":"string","minLength":8},"required":false,"name":"guestId","in":"query"}],"responses":{"200":{"description":"Mock test questions or plan limit response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"chapterIndex":{"type":"integer","minimum":1},"chapter":{"type":"string","minLength":1},"prompt":{"type":"string","minLength":1},"choices":{"type":"array","items":{"type":"string","minLength":1},"minItems":2},"requiredSelections":{"type":"integer","minimum":1}},"required":["id","chapterIndex","chapter","prompt","choices","requiredSelections"]}},"totalQuestions":{"type":"integer","minimum":0},"timeLimitMinutes":{"type":"integer","minimum":1},"passPercentage":{"type":"integer","minimum":0,"maximum":100},"degraded":{"type":"boolean"}},"required":["questions","totalQuestions","timeLimitMinutes","passPercentage"]},{"type":"object","properties":{"limited":{"type":"boolean","enum":[true]},"remaining":{"type":"integer","minimum":0},"upgrade":{"type":"string","minLength":1},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"chapterIndex":{"type":"integer","minimum":1},"chapter":{"type":"string","minLength":1},"prompt":{"type":"string","minLength":1},"choices":{"type":"array","items":{"type":"string","minLength":1},"minItems":2},"requiredSelections":{"type":"integer","minimum":1}},"required":["id","chapterIndex","chapter","prompt","choices","requiredSelections"]},"default":[]}},"required":["limited","remaining","upgrade"]}]}}}},"401":{"description":"Authentication or guest id required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Mock service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/mock/results":{"post":{"tags":["Mock"],"summary":"Submit mock test results","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"answers":{"type":"array","items":{"type":"object","properties":{"questionId":{"type":"string","minLength":1},"chosenIndex":{"type":"integer","minimum":0},"chosenIndexes":{"type":"array","items":{"type":"integer","minimum":0}},"correct":{"type":"boolean"},"chapter":{"type":"string","minLength":1}},"required":["questionId"]}},"score":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0},"passed":{"type":"boolean"},"timeUsedSeconds":{"type":"integer","minimum":0},"guestId":{"type":"string","minLength":8}},"required":["answers","timeUsedSeconds"]}}}},"responses":{"200":{"description":"Computed mock test result","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"result":{"type":"object","properties":{"score":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0},"passed":{"type":"boolean"},"percentage":{"type":"integer","minimum":0,"maximum":100},"breakdown":{"type":"object","additionalProperties":{"type":"object","properties":{"correct":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0}},"required":["correct","total"]}},"missedQuestions":{"type":"array","items":{"type":"object","properties":{"questionId":{"type":"string","minLength":1},"prompt":{"type":"string","minLength":1},"correctChoices":{"type":"array","items":{"type":"string","minLength":1}},"explanation":{"type":"string","minLength":1},"chapter":{"type":"string","minLength":1}},"required":["questionId","prompt","correctChoices","explanation","chapter"]}},"degraded":{"type":"boolean"}},"required":["score","total","passed","percentage","breakdown","missedQuestions"]}},"required":["ok","result"]}}}},"400":{"description":"Invalid mock submission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication or guest id required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Mock persistence unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}},"get":{"tags":["Mock"],"summary":"Fetch mock test history","responses":{"200":{"description":"Mock test history","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"score":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0},"passed":{"type":"boolean"},"percentage":{"type":"integer","minimum":0,"maximum":100},"completedAt":{"type":"string","nullable":true,"minLength":1}}}}},"required":["results"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Mock history unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/dashboard":{"get":{"tags":["Dashboard"],"summary":"Fetch dashboard analytics summary","responses":{"200":{"description":"Learner dashboard summary","content":{"application/json":{"schema":{"type":"object","properties":{"streak":{"type":"integer","minimum":0},"totalQuestions":{"type":"integer","minimum":0},"totalCorrect":{"type":"integer","minimum":0},"accuracy":{"type":"integer","minimum":0,"maximum":100},"readiness":{"type":"integer","minimum":0,"maximum":100},"chapterStats":{"type":"object","additionalProperties":{"type":"object","properties":{"correct":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0}},"required":["correct","total"]}},"mockResults":{"type":"array","items":{"type":"object","properties":{"score":{"type":"integer","minimum":0},"total":{"type":"integer","minimum":0},"passed":{"type":"boolean"},"percentage":{"type":"integer","minimum":0,"maximum":100},"completedAt":{"type":"string","nullable":true,"minLength":1}}}},"plan":{"type":"string","enum":["free","pro","lifetime"]},"analyticsTier":{"type":"string","enum":["basic","full"]}},"required":["streak","totalQuestions","totalCorrect","accuracy","readiness","chapterStats","mockResults","plan"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Dashboard unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/onboarding":{"get":{"tags":["Onboarding"],"summary":"Get onboarding answers for the current user","responses":{"200":{"description":"Saved onboarding answers","content":{"application/json":{"schema":{"type":"object","properties":{"onboarding":{"type":"object","nullable":true,"properties":{"reason":{"type":"string","minLength":1},"goal":{"type":"string","minLength":1},"start":{"type":"string","minLength":1}},"required":["reason","goal","start"]}},"required":["onboarding"]}}}},"400":{"description":"Invalid user id","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Onboarding service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}},"post":{"tags":["Onboarding"],"summary":"Save onboarding answers for the current user","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","minLength":1},"goal":{"type":"string","minLength":1},"start":{"type":"string","minLength":1}},"required":["reason","goal","start"]}}}},"responses":{"200":{"description":"Onboarding answers saved","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}}}},"400":{"description":"Invalid onboarding payload","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Onboarding service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/stripe/checkout":{"post":{"tags":["Billing"],"summary":"Create Stripe checkout session","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["lifetime"]}}}}}},"responses":{"200":{"description":"Checkout redirect url","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"400":{"description":"Invalid checkout request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Billing service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/stripe/portal":{"post":{"tags":["Billing"],"summary":"Create Stripe billing portal session","responses":{"200":{"description":"Billing portal redirect url","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]}}}},"400":{"description":"Billing portal is not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"404":{"description":"Billing account not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Billing service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}},"/api/v1/stripe/webhook":{"post":{"tags":["Billing"],"summary":"Receive Stripe webhooks for plan synchronization","responses":{"200":{"description":"Webhook accepted","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean","enum":[true]}},"required":["received"]}}}},"400":{"description":"Invalid Stripe webhook","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}},"503":{"description":"Billing service unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","minLength":1}},"required":["error"]}}}}}}}}}