{"openapi":"3.1.0","info":{"title":"Polymath API","version":"0.1.0","description":"Polymath public + institutional API. Documents the endpoints schools, donors, and integration partners depend on (NCP coverage, credential verification, course catalog, public portfolios, auth). Internal admin and platform-private routes are intentionally not documented in v0.1; see ADR 0022.","contact":{"name":"Polymath Engineering","email":"dev@polymath.dev"},"license":{"name":"Proprietary","identifier":"LicenseRef-Polymath-Proprietary"}},"servers":[{"url":"https://api.polymath.dev","description":"Production"},{"url":"https://api.staging.polymath.dev","description":"Staging"},{"url":"http://localhost:3001","description":"Local development"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"opaque","description":"Better Auth session token. Required for /v1 routes that are not marked public."},"cookieAuth":{"type":"apiKey","in":"cookie","name":"better-auth.session_token","description":"Browser session cookie set on sign-in."}},"schemas":{"Health":{"type":"object","properties":{"ok":{"type":"boolean"},"ts":{"type":"string","format":"date-time","example":"2026-04-30T12:00:00.000Z"},"db":{"type":"boolean"}},"required":["ok"]},"Error500":{"type":"object","properties":{"error":{"type":"string","example":"not found"},"issues":{"type":"array","items":{"nullable":true}}},"required":["error"]},"NcpSlo":{"type":"object","properties":{"code":{"type":"string","example":"G7-MTH-NS-1.1"},"grade":{"type":"integer"},"domain":{"type":"string"},"statementEn":{"type":"string"},"statementUr":{"type":"string","nullable":true}},"required":["code","grade","domain","statementEn"]},"Error400":{"type":"object","properties":{"error":{"type":"string","example":"not found"},"issues":{"type":"array","items":{"nullable":true}}},"required":["error"]},"Lesson":{"type":"object","properties":{"id":{"type":"string"},"courseId":{"type":"string"},"titleEn":{"type":"string"},"titleUr":{"type":"string","nullable":true},"position":{"type":"integer","minimum":0},"lessonKind":{"type":"string"},"ncpSloCodes":{"type":"array","items":{"type":"string"}}},"required":["id","courseId","titleEn"]},"Error404":{"type":"object","properties":{"error":{"type":"string","example":"not found"},"issues":{"type":"array","items":{"nullable":true}}},"required":["error"]},"CoverageReport":{"type":"object","properties":{"summary":{"type":"object","properties":{"totalSlos":{"type":"integer"},"totalCovered":{"type":"integer"},"coveragePct":{"type":"integer","minimum":0,"maximum":100}},"required":["totalSlos","totalCovered","coveragePct"]},"cells":{"type":"array","items":{"type":"object","properties":{"grade":{"type":"integer"},"domain":{"type":"string"},"total":{"type":"integer"},"covered":{"type":"integer"}},"required":["grade","domain","total","covered"]}}},"required":["summary","cells"]},"ProvincialChapter":{"type":"object","properties":{"id":{"type":"string"},"board":{"type":"string","enum":["punjab","sindh","kp","balochistan","federal","gb","ajk"]},"grade":{"type":"integer"},"chapterCode":{"type":"string"},"chapterTitle":{"type":"string"},"sloCodes":{"type":"array","items":{"type":"string"}}},"required":["id","board","grade","chapterCode"]},"Course":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"titleEn":{"type":"string"},"titleUr":{"type":"string","nullable":true},"descriptionEn":{"type":"string","nullable":true},"status":{"type":"string","enum":["draft","review","published","archived"]},"audienceCodes":{"type":"array","items":{"type":"string"}},"competencyCodes":{"type":"array","items":{"type":"string"}},"languageSupport":{"type":"array","items":{"type":"string","enum":["en","ur"]}},"totalHours":{"type":"integer","nullable":true,"minimum":0,"exclusiveMinimum":true},"creditValue":{"type":"integer","nullable":true,"minimum":0,"exclusiveMinimum":true},"publishedAt":{"type":"string","nullable":true,"format":"date-time","example":"2026-04-30T12:00:00.000Z"},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/Lesson"}}},"required":["id","code","titleEn"]},"Error401":{"type":"object","properties":{"error":{"type":"string","example":"not found"},"issues":{"type":"array","items":{"nullable":true}}},"required":["error"]},"Error403":{"type":"object","properties":{"error":{"type":"string","example":"not found"},"issues":{"type":"array","items":{"nullable":true}}},"required":["error"]},"Error429":{"type":"object","properties":{"error":{"type":"string","example":"not found"},"issues":{"type":"array","items":{"nullable":true}}},"required":["error"]},"Credential":{"type":"object","properties":{"id":{"type":"string"},"subjectUserId":{"type":"string"},"courseId":{"type":"string","nullable":true},"issuedAt":{"type":"string","format":"date-time","example":"2026-04-30T12:00:00.000Z"},"revokedAt":{"type":"string","nullable":true,"format":"date-time","example":"2026-04-30T12:00:00.000Z"},"vc":{"nullable":true,"description":"W3C Verifiable Credential JSON-LD document"}},"required":["id","subjectUserId"]},"VerifyCredentialResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"verified":{"type":"boolean"},"credential":{"$ref":"#/components/schemas/Credential"},"reason":{"type":"string","nullable":true}},"required":["ok","verified"]},"PortfolioPublic":{"type":"object","properties":{"slug":{"type":"string"},"displayName":{"type":"string"},"bio":{"type":"string","nullable":true},"artifacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"mediaUrl":{"type":"string","nullable":true,"format":"uri"},"createdAt":{"type":"string","format":"date-time","example":"2026-04-30T12:00:00.000Z"}},"required":["id","title"]}},"credentials":{"type":"array","items":{"$ref":"#/components/schemas/Credential"}}},"required":["slug","displayName"]},"User":{"type":"object","properties":{"id":{"type":"string","example":"usr_abc123"},"email":{"type":"string","format":"email"},"name":{"type":"string","nullable":true},"role":{"type":"string","enum":["student","parent","teacher","org_admin","platform_admin","donor"]},"createdAt":{"type":"string","format":"date-time","example":"2026-04-30T12:00:00.000Z"}},"required":["id","email"]},"SessionResponse":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"session":{"type":"object","properties":{"token":{"type":"string"},"expiresAt":{"type":"string","format":"date-time","example":"2026-04-30T12:00:00.000Z"}}}},"required":["user"]},"SignInRequest":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8}},"required":["email","password"]},"SignUpRequest":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8},"name":{"type":"string","minLength":1}},"required":["email","password","name"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["Health"],"summary":"Liveness probe","responses":{"200":{"description":"API is up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/health/deep":{"get":{"tags":["Health"],"summary":"Readiness probe (database)","responses":{"200":{"description":"DB reachable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/ncp/slos":{"get":{"tags":["NCP"],"summary":"List NCP Student Learning Outcomes","description":"Public. Schools and regulators use this to inspect Polymath's SLO catalog.","parameters":[{"schema":{"type":"string","example":"7"},"required":false,"in":"query","name":"grade"},{"schema":{"type":"string"},"required":false,"in":"query","name":"domain"},{"schema":{"type":"string"},"required":false,"in":"query","name":"search"}],"responses":{"200":{"description":"SLO list","content":{"application/json":{"schema":{"type":"object","properties":{"slos":{"type":"array","items":{"$ref":"#/components/schemas/NcpSlo"}}},"required":["slos"]}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error400"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/ncp/slos/{code}":{"get":{"tags":["NCP"],"summary":"Get a single SLO and the lessons covering it","parameters":[{"schema":{"type":"string","example":"G7-MTH-NS-1.1"},"required":true,"name":"code","in":"path"}],"responses":{"200":{"description":"SLO + covering lessons","content":{"application/json":{"schema":{"type":"object","properties":{"slo":{"$ref":"#/components/schemas/NcpSlo"},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/Lesson"}}},"required":["slo","lessons"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error404"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/ncp/coverage":{"get":{"tags":["NCP"],"summary":"Coverage matrix: how many SLOs have at least one lesson","description":"Public. Donors and the Ministry of Federal Education use this to track NCP coverage progress.","parameters":[{"schema":{"type":"string"},"required":false,"in":"query","name":"grade"}],"responses":{"200":{"description":"Coverage report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverageReport"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/ncp/provincial-chapters":{"get":{"tags":["NCP"],"summary":"Provincial textbook chapter to SLO mapping","parameters":[{"schema":{"type":"string"},"required":false,"in":"query","name":"board"},{"schema":{"type":"string"},"required":false,"in":"query","name":"grade"}],"responses":{"200":{"description":"Chapter mappings","content":{"application/json":{"schema":{"type":"object","properties":{"chapters":{"type":"array","items":{"$ref":"#/components/schemas/ProvincialChapter"}}},"required":["chapters"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/courses":{"get":{"tags":["Courses"],"summary":"List published courses","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"responses":{"200":{"description":"Published courses","content":{"application/json":{"schema":{"type":"object","properties":{"courses":{"type":"array","items":{"$ref":"#/components/schemas/Course"}}},"required":["courses"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error401"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error403"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error429"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/courses/{id}":{"get":{"tags":["Courses"],"summary":"Get a course with its lessons","security":[{"bearerAuth":[]},{"cookieAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Course with lessons","content":{"application/json":{"schema":{"type":"object","properties":{"course":{"$ref":"#/components/schemas/Course"}},"required":["course"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error401"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error403"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error404"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/credentials/verify/{token}":{"get":{"tags":["Credentials"],"summary":"Verify a Polymath-issued Verifiable Credential","description":"Public. Anyone holding a credential token (printed on transcripts and embedded in QR codes) can verify authenticity without authentication.","parameters":[{"schema":{"type":"string","example":"cred_eyJhbGciOi..."},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"Verification result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyCredentialResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error404"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/p/{slug}":{"get":{"tags":["Portfolio"],"summary":"Public portfolio profile","description":"Public. Learner-controlled showcase of artifacts and credentials at /p/{slug}.","parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Public portfolio","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioPublic"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error404"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/auth/sign-in/email":{"post":{"tags":["Auth"],"summary":"Sign in with email + password","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInRequest"}}}},"responses":{"200":{"description":"Session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error400"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error401"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error429"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}},"/v1/auth/sign-up/email":{"post":{"tags":["Auth"],"summary":"Sign up with email + password","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignUpRequest"}}}},"responses":{"200":{"description":"Session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error400"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error429"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error500"}}}}}}}}}