{"openapi":"3.0.0","paths":{"/debug-sentry":{"get":{"operationId":"RootController_getError","parameters":[],"responses":{"200":{"description":""}}}},"/":{"get":{"operationId":"RootController_index","parameters":[],"responses":{"200":{"description":""}}}},"/health":{"get":{"operationId":"HealthController_check","parameters":[],"responses":{"200":{"description":""}}}},"/users/me":{"get":{"operationId":"UsersController_getMe","summary":"Get the current user with stats","parameters":[{"name":"timezone","required":false,"in":"query","description":"IANA timezone (e.g. America/New_York). Used to determine the user's local date.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetailDto"}}}}},"tags":["users"],"security":[{"bearer":[]}]},"patch":{"operationId":"UsersController_updateMe","summary":"Update the current user's profile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["users"],"security":[{"bearer":[]}]}},"/users":{"put":{"operationId":"UsersController_upsert","summary":"Create or update a user by email","parameters":[{"name":"authorization","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":"User already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}},"201":{"description":"User created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponseDto"}}}}},"tags":["users"],"security":[{"bearer":[]}]}},"/users/{id}/role":{"patch":{"operationId":"UsersController_updateRole","summary":"Promote or demote a user (admin ↔ user)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRoleDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRoleResponseDto"}}}},"403":{"description":"Cannot change your own role"},"404":{"description":"User not found"}},"tags":["users"],"security":[{"bearer":[]}]}},"/users/{id}":{"delete":{"operationId":"UsersController_deleteUser","summary":"Hard-delete a user and all owned data","description":"Removes the user row plus every dependent row (enrollments, completions, recall cards/schedules, daily activity, stats, comprehension attempts, daily schedules). CASCADE FKs clean up user_events, push_*, and authored review comments.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"User deleted"},"403":{"description":"Cannot delete yourself"},"404":{"description":"User not found"}},"tags":["users"],"security":[{"bearer":[]}]}},"/paths":{"post":{"operationId":"PathsController_create","summary":"Create a path","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePathDto"}}}},"responses":{"201":{"description":"Path created"}},"tags":["paths"],"security":[{"bearer":[]}]},"get":{"operationId":"PathsController_findAll","summary":"List published paths","parameters":[],"responses":{"200":{"description":"List of published paths","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PathViewDto"}}}}}},"tags":["paths"],"security":[{"bearer":[]}]}},"/paths/drafts":{"get":{"operationId":"PathsController_findDrafts","summary":"List unpublished (draft) paths","parameters":[],"responses":{"200":{"description":"List of draft paths","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DraftPathViewDto"}}}}}},"tags":["paths"],"security":[{"bearer":[]}]}},"/paths/{id}":{"get":{"operationId":"PathsController_findOne","summary":"Get a path by ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Path found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PathDetailDto"}}}},"404":{"description":"Path not found"}},"tags":["paths"],"security":[{"bearer":[]}]},"patch":{"operationId":"PathsController_update","summary":"Update a path","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePathDto"}}}},"responses":{"200":{"description":"Path updated"},"404":{"description":"Path not found"}},"tags":["paths"],"security":[{"bearer":[]}]},"delete":{"operationId":"PathsController_remove","summary":"Soft-delete a draft path","description":"Marks the path as deleted (sets deleted_at and deleted_by). Only drafts can be deleted; published paths must be unpublished first.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Path deleted"},"400":{"description":"Path is published"},"404":{"description":"Path not found"}},"tags":["paths"],"security":[{"bearer":[]}]}},"/paths/{id}/publish":{"patch":{"operationId":"PathsController_publish","summary":"Publish a path","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Path published"},"400":{"description":"Not all lessons are approved"},"404":{"description":"Path not found"}},"tags":["paths"],"security":[{"bearer":[]}]}},"/paths/{id}/unpublish":{"patch":{"operationId":"PathsController_unpublish","summary":"Unpublish a path","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Path unpublished"},"404":{"description":"Path not found"}},"tags":["paths"],"security":[{"bearer":[]}]}},"/topics":{"post":{"operationId":"TopicsController_create","summary":"Create a topic","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTopicDto"}}}},"responses":{"201":{"description":"Topic created"}},"tags":["topics"],"security":[{"bearer":[]}]},"get":{"operationId":"TopicsController_findAll","summary":"List all topics","parameters":[],"responses":{"200":{"description":"List of topics","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TopicViewDto"}}}}}},"tags":["topics"],"security":[{"bearer":[]}]}},"/topics/{id}":{"get":{"operationId":"TopicsController_findOne","summary":"Get a topic by ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Topic found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopicDetailDto"}}}},"404":{"description":"Topic not found"}},"tags":["topics"],"security":[{"bearer":[]}]},"patch":{"operationId":"TopicsController_update","summary":"Update a topic","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTopicDto"}}}},"responses":{"200":{"description":"Topic updated"},"404":{"description":"Topic not found"}},"tags":["topics"],"security":[{"bearer":[]}]},"delete":{"operationId":"TopicsController_remove","summary":"Delete a topic","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Topic deleted"},"404":{"description":"Topic not found"}},"tags":["topics"],"security":[{"bearer":[]}]}},"/lessons":{"post":{"operationId":"LessonsController_create","summary":"Create a lesson","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLessonDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonDetailDto"}}}}},"tags":["lessons"],"security":[{"bearer":[]}]}},"/lessons/{id}":{"get":{"operationId":"LessonsController_findOne","summary":"Get a lesson by ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonDetailDto"}}}},"404":{"description":"Lesson not found"}},"tags":["lessons"],"security":[{"bearer":[]}]},"patch":{"operationId":"LessonsController_update","summary":"Update a lesson","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLessonDto"}}}},"responses":{"200":{"description":"Lesson updated"},"404":{"description":"Lesson not found"}},"tags":["lessons"],"security":[{"bearer":[]}]},"delete":{"operationId":"LessonsController_remove","summary":"Delete a lesson","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Lesson deleted"},"404":{"description":"Lesson not found"}},"tags":["lessons"],"security":[{"bearer":[]}]}},"/lessons/{id}/comprehension-questions":{"get":{"operationId":"LessonsController_getComprehensionQuestions","summary":"Get comprehension questions for a lesson","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ComprehensionQuestionDto"}}}}},"404":{"description":"Lesson not found"}},"tags":["lessons"],"security":[{"bearer":[]}]},"post":{"operationId":"LessonsController_createComprehensionQuestions","summary":"Create comprehension questions for a lesson","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateComprehensionQuestionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ComprehensionQuestionDto"}}}}}},"tags":["lessons"],"security":[{"bearer":[]}]}},"/lessons/{id}/recall-questions":{"get":{"operationId":"LessonsController_getRecallQuestions","summary":"Get recall questions for a lesson","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecallQuestionDto"}}}}},"404":{"description":"Lesson not found"}},"tags":["lessons"],"security":[{"bearer":[]}]},"post":{"operationId":"LessonsController_createRecallQuestions","summary":"Create recall questions for a lesson","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRecallQuestionsDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecallQuestionDto"}}}}}},"tags":["lessons"],"security":[{"bearer":[]}]}},"/lessons/{lessonId}/comprehension-questions/{questionId}/attempts":{"post":{"operationId":"LessonsController_createComprehensionQuestionAttempt","summary":"Submit an attempt for a comprehension question","parameters":[{"name":"lessonId","required":true,"in":"path","schema":{"type":"string"}},{"name":"questionId","required":true,"in":"path","schema":{"type":"string"}},{"name":"user-agent","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateComprehensionQuestionAttemptDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComprehensionQuestionAttempt"}}}},"400":{"description":"Answer does not belong to the question"},"404":{"description":"Lesson or question not found"}},"tags":["lessons"],"security":[{"bearer":[]}]}},"/lessons/{id}/status":{"patch":{"operationId":"LessonsController_updateStatus","summary":"Update lesson reading progress for the current user","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"user-agent","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLessonStatusDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonStatusDto"}}}},"404":{"description":"Lesson not found"}},"tags":["lessons"],"security":[{"bearer":[]}]}},"/lessons/{id}/complete":{"post":{"operationId":"LessonsController_completeLesson","summary":"Mark lesson as complete for the current user — schedules recall cards","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"timezone","required":false,"in":"query","description":"IANA timezone (e.g. America/New_York). Used to determine the user's local date.","schema":{"type":"string"}},{"name":"user-agent","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonCompleteResponseDto"}}}},"404":{"description":"Lesson not found"}},"tags":["lessons"],"security":[{"bearer":[]}]}},"/recall/due":{"get":{"operationId":"RecallController_getDueCards","summary":"Get recall cards due today","parameters":[{"name":"timezone","required":false,"in":"query","description":"IANA timezone (e.g. America/New_York). Used to determine the user's local date.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DueRecallCardDto"}}}}}},"tags":["recall"],"security":[{"bearer":[]}]}},"/recall/review":{"post":{"operationId":"RecallController_submitReview","parameters":[{"name":"timezone","required":false,"in":"query","description":"IANA timezone (e.g. America/New_York). Used to determine the user's local date.","schema":{"type":"string"}},{"name":"user-agent","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitRecallReviewDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallReviewResultDto"}}}}},"tags":["recall"],"security":[{"bearer":[]}]}},"/schedule/today":{"get":{"operationId":"DailyScheduleController_getTodaySchedule","summary":"Get today's scheduled lessons (one per active enrollment)","parameters":[{"name":"timezone","required":false,"in":"query","description":"IANA timezone (e.g. America/New_York). Used to determine the user's local date.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DailyScheduleViewDto"}}}}}},"tags":["schedule"],"security":[{"bearer":[]}]}},"/enrollments":{"post":{"operationId":"EnrollmentsController_create","summary":"Enroll current user in a path or topic","parameters":[{"name":"user-agent","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEnrollmentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserEnrollment"}}}}},"tags":["enrollments"],"security":[{"bearer":[]}]}},"/enrollments/me":{"get":{"operationId":"EnrollmentsController_getMyEnrollments","summary":"List current user's active enrollments","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EnrollmentResponseDto"}}}}}},"tags":["enrollments"],"security":[{"bearer":[]}]}},"/enrollments/{id}":{"delete":{"operationId":"EnrollmentsController_remove","summary":"Unenroll (soft-delete) an enrollment by ID","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"user-agent","required":true,"in":"header","schema":{"type":"string"}},{"name":"timezone","required":false,"in":"query","description":"IANA timezone (e.g. America/New_York). Used to determine the user's local date.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserEnrollment"}}}}},"tags":["enrollments"],"security":[{"bearer":[]}]}},"/discover":{"get":{"operationId":"DiscoverController_discover","summary":"Get discovery items for home page: 1 new, 1 trending, 1 popular path","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoverResponseDto"}}}}},"tags":["discover"],"security":[{"bearer":[]}]}},"/content-generation/outline":{"post":{"operationId":"ContentGenerationController_generateOutline","summary":"Generate a content outline (titles only — fast, cheap to retry)","description":"Phase 1 of content generation. Returns a tree of path/topic/lesson titles and descriptions. The outline can be edited before proceeding to Phase 2.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateOutlineRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateOutlineResponseDto"}}}}},"tags":["content-generation"],"security":[{"bearer":[]}]}},"/content-generation/suggestions":{"post":{"operationId":"ContentGenerationController_suggestPaths","summary":"Suggest new learning path ideas for a field","description":"Returns path name suggestions based on the given field, existing paths in the database, and current industry trends.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestPathsRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestPathsResponseDto"}}}}},"tags":["content-generation"],"security":[{"bearer":[]}]}},"/content-generation/content":{"post":{"operationId":"ContentGenerationController_generateContent","summary":"Generate full lesson content from an approved outline","description":"Phase 2 of content generation. Takes an outline (from /content-generation/outline, possibly edited) and generates full content for every lesson in parallel. Returns a preview — nothing is saved until the admin confirms.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateContentRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateContentResponseDto"}}}}},"tags":["content-generation"],"security":[{"bearer":[]}]}},"/content-generation/save-draft":{"post":{"operationId":"ContentGenerationController_saveDraft","summary":"Save an outline as draft","description":"Creates a path (in path mode) or attaches to an existing path (in topic mode), plus all topics and lessons in a single transaction. Each lesson gets a draft review record.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveOutlineDraftRequestDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveOutlineDraftResponseDto"}}}},"400":{"description":"Validation error or missing pathId for topic mode"},"404":{"description":"Path not found (topic mode)"}},"tags":["content-generation"],"security":[{"bearer":[]}]}},"/content-generation/lesson":{"post":{"operationId":"ContentGenerationController_generateLessonContent","summary":"Generate content for a single draft lesson","description":"Fetches the lesson (must be in 'draft' status), generates content via Claude, saves it to the lesson, and sets review_status to 'generated'.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateLessonContentRequestDto"}}}},"responses":{"200":{"description":"Updated lesson with generated content"},"400":{"description":"Lesson is not in draft status"},"404":{"description":"Lesson not found"}},"tags":["content-generation"],"security":[{"bearer":[]},{"bearer":[]}]}},"/content-generation/lessons/batch":{"post":{"operationId":"ContentGenerationController_generateLessonsBatch","summary":"Generate content for multiple draft lessons","description":"Generates content for each lesson independently. If one fails, others still succeed. Returns per-lesson results.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateLessonsBatchRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateLessonsBatchResponseDto"}}}}},"tags":["content-generation"],"security":[{"bearer":[]},{"bearer":[]}]}},"/content-generation/content-block":{"post":{"operationId":"ContentGenerationController_regenerateContentBlock","summary":"Regenerate a single content block or selection within it","description":"When selection is omitted, regenerates the entire block. When selection is provided, regenerates only the selected portion. Does not change review_status.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateContentBlockRequestDto"}}}},"responses":{"200":{"description":"The updated block"},"400":{"description":"Invalid block index or selection"},"404":{"description":"Lesson not found"}},"tags":["content-generation"],"security":[{"bearer":[]},{"bearer":[]}]}},"/content-generation/topics/{topicId}/suggest-lessons":{"post":{"operationId":"ContentGenerationController_suggestLessons","summary":"Suggest and save additional draft lessons for an existing topic","description":"Uses the outline AI to suggest N new lesson titles and descriptions for the given topic, taking into account existing lessons to avoid duplicates. Suggestions are persisted as draft lessons appended to the end of the topic.","parameters":[{"name":"topicId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestLessonsRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestLessonsResponseDto"}}}},"404":{"description":"Topic not found"}},"tags":["content-generation"],"security":[{"bearer":[]}]}},"/content-generation/export/path/{pathId}":{"get":{"operationId":"ContentGenerationController_exportPath","summary":"Export a path and all of its content as JSON","description":"Returns the full path tree (topics, lessons, content blocks, comprehension and recall questions) plus per-lesson review status. Review comments and all IDs/timestamps are stripped; the output round-trips through POST /import/path.","parameters":[{"name":"pathId","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PathExportDto"}}}},"404":{"description":"Path not found"}},"tags":["content-generation"],"security":[{"bearer":[]}]}},"/content-generation/import/path":{"post":{"operationId":"ContentGenerationController_importPath","summary":"Import a path exported via GET /export/path/:pathId","description":"Upserts on path.pathId: if the export carries a pathId that matches an existing path, the path's title/description/emoji are updated and all of its child content (topics, lessons, reviews, comprehension and recall questions) is replaced from the import — note that this regenerates child UUIDs. Otherwise creates a brand-new path with fresh UUIDs and isPublished=false. Review status is preserved per lesson. The response operation field indicates which branch ran.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PathExportDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportPathResponseDto"}}}},"400":{"description":"Validation error (bad shape or unsupported version)"}},"tags":["content-generation"],"security":[{"bearer":[]}]}},"/review/lessons/{lessonId}":{"get":{"operationId":"ReviewController_getReviewStatus","summary":"Get review status for a lesson","parameters":[{"name":"lessonId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonReviewResponseDto"}}}},"404":{"description":"Review not found"}},"tags":["review"],"security":[{"bearer":[]}]}},"/review/lessons/{lessonId}/status":{"patch":{"operationId":"ReviewController_updateReviewStatus","summary":"Update review status for a lesson","parameters":[{"name":"lessonId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateReviewStatusRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonReviewResponseDto"}}}},"400":{"description":"Invalid status transition"},"404":{"description":"Review not found"}},"tags":["review"],"security":[{"bearer":[]}]}},"/review/lessons/{lessonId}/comments":{"get":{"operationId":"ReviewController_getComments","summary":"Get review comments for a lesson","parameters":[{"name":"lessonId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LessonReviewCommentResponseDto"}}}}},"404":{"description":"Lesson not found"}},"tags":["review"],"security":[{"bearer":[]}]},"post":{"operationId":"ReviewController_createComment","summary":"Add a review comment to a lesson content block","parameters":[{"name":"lessonId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLessonCommentRequestDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonReviewCommentResponseDto"}}}},"404":{"description":"Lesson not found"}},"tags":["review"],"security":[{"bearer":[]}]}},"/review/comments/{id}":{"patch":{"operationId":"ReviewController_updateComment","summary":"Update a review comment (edit text or resolve)","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLessonCommentRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonReviewCommentResponseDto"}}}},"404":{"description":"Comment not found"}},"tags":["review"],"security":[{"bearer":[]}]},"delete":{"operationId":"ReviewController_deleteComment","summary":"Delete a review comment","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Comment deleted"},"403":{"description":"Only the author can delete"},"404":{"description":"Comment not found"}},"tags":["review"],"security":[{"bearer":[]}]}},"/tracking/events":{"post":{"operationId":"TrackingController_trackEvents","summary":"Track a batch of user events from the client","parameters":[{"name":"user-agent","required":true,"in":"header","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchTrackEventsDto"}}}},"responses":{"202":{"description":"Events accepted"}},"tags":["tracking"],"security":[{"bearer":[]}]}},"/metrics":{"get":{"operationId":"PrometheusController_index","parameters":[],"responses":{"200":{"description":""}}}},"/admin/users/analytics":{"get":{"operationId":"UsersAnalyticsController_getTopUsers","summary":"List top users ranked by composite engagement score","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"offset","required":false,"in":"query","schema":{"type":"number"}},{"name":"q","required":false,"in":"query","description":"Case-insensitive substring match on name or email.","schema":{"type":"string"}},{"name":"sortBy","required":false,"in":"query","description":"Column to order by. Defaults to engagementScore.","schema":{"$ref":"#/components/schemas/UserAnalyticsSortBy"}},{"name":"direction","required":false,"in":"query","description":"Sort direction. Defaults to desc.","schema":{"$ref":"#/components/schemas/SortDirection"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopUsersResponseDto"}}}}},"tags":["admin"],"security":[{"bearer":[]}]}},"/admin/users/{id}/journey":{"get":{"operationId":"UsersAnalyticsController_getUserJourney","summary":"Get a user's journey: curated milestones plus a paginated raw event feed","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"before","required":false,"in":"query","description":"ISO timestamp cursor returned as nextCursor by a prior call","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserJourneyDto"}}}},"404":{"description":"User not found"}},"tags":["admin"],"security":[{"bearer":[]}]}},"/admin/marketing/funnel/summary":{"get":{"operationId":"MarketingFunnelController_getSummary","summary":"Marketing funnel totals + previous-window delta + by-source","parameters":[{"name":"range","required":false,"in":"query","schema":{"$ref":"#/components/schemas/MarketingFunnelRange"}},{"name":"source","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketingFunnelSummaryDto"}}}}},"tags":["admin-marketing"],"security":[{"bearer":[]}]}},"/admin/marketing/funnel/timeseries":{"get":{"operationId":"MarketingFunnelController_getTimeseries","summary":"Leads + conversions bucketed for charting (zero-filled)","parameters":[{"name":"range","required":false,"in":"query","schema":{"$ref":"#/components/schemas/MarketingFunnelRange"}},{"name":"source","required":false,"in":"query","schema":{"type":"string"}},{"name":"bucket","required":false,"in":"query","description":"Defaults to 'day' for 7d/30d, 'week' for 90d/all.","schema":{"$ref":"#/components/schemas/MarketingFunnelBucket"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketingFunnelTimeseriesDto"}}}}},"tags":["admin-marketing"],"security":[{"bearer":[]}]}},"/admin/marketing/leads":{"get":{"operationId":"MarketingFunnelController_getLeads","summary":"Paginated leads with conversion status (matching user, if any)","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"offset","required":false,"in":"query","schema":{"type":"number"}},{"name":"source","required":false,"in":"query","schema":{"type":"string"}},{"name":"q","required":false,"in":"query","description":"Case-insensitive substring match on email or name.","schema":{"type":"string"}},{"name":"converted","required":false,"in":"query","schema":{"$ref":"#/components/schemas/MarketingFunnelConvertedFilter"}},{"name":"sortBy","required":false,"in":"query","schema":{"$ref":"#/components/schemas/MarketingFunnelSortBy"}},{"name":"direction","required":false,"in":"query","schema":{"$ref":"#/components/schemas/SortDirection"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketingFunnelLeadsListDto"}}}}},"tags":["admin-marketing"],"security":[{"bearer":[]}]}},"/admin/marketing/sources":{"get":{"operationId":"MarketingFunnelController_getSources","summary":"Distinct lead sources ordered by lead count desc","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketingFunnelSourcesDto"}}}}},"tags":["admin-marketing"],"security":[{"bearer":[]}]}},"/admin/emails/welcome":{"post":{"operationId":"EmailController_welcome","summary":"Send a welcome email to an address (manual/test trigger).","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendWelcomeDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailResponseDto"}}}}},"tags":["admin"],"security":[{"bearer":[]}]}},"/admin/emails/invite":{"post":{"operationId":"EmailController_invite","summary":"Send a private-beta invite email. Access control stays in Auth0; this only sends the email.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendBetaInviteDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailResponseDto"}}}}},"tags":["admin"],"security":[{"bearer":[]}]}},"/me/push-tokens":{"post":{"operationId":"PushNotificationsController_registerToken","summary":"Register or re-register a push token for the current user","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterPushTokenDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushTokenResponseDto"}}}}},"tags":["push-notifications"],"security":[{"bearer":[]}]}},"/me/push-tokens/{nativeToken}":{"delete":{"operationId":"PushNotificationsController_disableTokenByNativeToken","summary":"Disable a push token by its native (Expo) token string","parameters":[{"name":"nativeToken","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"tags":["push-notifications"],"security":[{"bearer":[]}]}},"/me/push-notification-prefs":{"get":{"operationId":"PushNotificationsController_getPrefs","summary":"List all push notification preferences for the user","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushNotificationPrefListDto"}}}}},"tags":["push-notifications"],"security":[{"bearer":[]}]},"patch":{"operationId":"PushNotificationsController_updatePref","summary":"Update a single push notification preference","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePushNotificationPrefDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushNotificationPrefDto"}}}}},"tags":["push-notifications"],"security":[{"bearer":[]}]}},"/me/push-notification-prefs/all":{"patch":{"operationId":"PushNotificationsController_setPushNotificationsEnabled","summary":"Toggle all push notification preferences for the user at once","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPushNotificationsEnabledDto"}}}},"responses":{"204":{"description":""}},"tags":["push-notifications"],"security":[{"bearer":[]}]}},"/admin/push-notifications/test":{"post":{"operationId":"PushNotificationTestController_test","summary":"Send a one-off test push notification to a user. Bypasses scheduler gating but still respects the idempotency log.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushNotificationTestDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushNotificationTestResponseDto"}}}}},"tags":["admin"],"security":[{"bearer":[]}]}},"/marketing/leads":{"post":{"operationId":"MarketingLeadsController_create","summary":"Capture a marketing lead (name + email)","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMarketingLeadDto"}}}},"responses":{"200":{"description":"Existing lead updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketingLeadResponseDto"}}}},"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketingLeadResponseDto"}}}}},"tags":["marketing-leads"]}}},"info":{"title":"Kursio API","description":"Kursio REST API documentation","version":"0.1.0","contact":{}},"tags":[],"servers":[],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"DailyActivityDto":{"type":"object","properties":{"day":{"type":"string","example":"Mon"},"fullDate":{"type":"string","example":"2026-03-15"},"active":{"type":"boolean"}},"required":["day","fullDate","active"]},"StreakDto":{"type":"object","properties":{"current":{"type":"number"},"best":{"type":"number"},"weeklyActivity":{"type":"array","items":{"$ref":"#/components/schemas/DailyActivityDto"}}},"required":["current","best","weeklyActivity"]},"TodayActivityDto":{"type":"object","properties":{"lessonsFinished":{"type":"number"},"recallFinished":{"type":"number"}},"required":["lessonsFinished","recallFinished"]},"UserDetailDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"avatarUrl":{"type":"string","nullable":true},"role":{"type":"string","enum":["user","admin"]},"domain":{"type":"string","nullable":true},"experience":{"type":"string","nullable":true},"onboardingCompleted":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"lessonsCompleted":{"type":"number"},"topicsCompleted":{"type":"number"},"cardsRecalled":{"type":"number"},"cardsMastered":{"type":"number"},"streak":{"$ref":"#/components/schemas/StreakDto"},"todayActivity":{"$ref":"#/components/schemas/TodayActivityDto"}},"required":["id","name","email","avatarUrl","role","domain","experience","onboardingCompleted","createdAt","updatedAt","lessonsCompleted","topicsCompleted","cardsRecalled","cardsMastered","streak","todayActivity"]},"UpdateUserDto":{"type":"object","properties":{"domain":{"type":"string"},"experience":{"type":"string"},"onboardingCompleted":{"type":"boolean"}}},"UserResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"avatarUrl":{"type":"string","nullable":true},"role":{"type":"string","enum":["user","admin"]},"domain":{"type":"string","nullable":true},"experience":{"type":"string","nullable":true},"onboardingCompleted":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","name","email","avatarUrl","role","domain","experience","onboardingCompleted","createdAt","updatedAt"]},"UpdateUserRoleDto":{"type":"object","properties":{"role":{"type":"string","enum":["user","admin"]}},"required":["role"]},"UpdateUserRoleResponseDto":{"type":"object","properties":{"id":{"type":"string"},"role":{"type":"string","enum":["user","admin"]}},"required":["id","role"]},"CreatePathDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"}},"required":["title","description","emoji"]},"PathViewDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"isPublished":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"topicCount":{"type":"number"},"lessonCount":{"type":"number"},"totalEstimatedReadTimeInMinutes":{"type":"number"},"totalRecallQuestionsCount":{"type":"number"},"enrollmentStatus":{"type":"string","enum":["not_enrolled","active","completed"]}},"required":["id","title","description","emoji","isPublished","createdAt","updatedAt","topicCount","lessonCount","totalEstimatedReadTimeInMinutes","totalRecallQuestionsCount","enrollmentStatus"]},"LessonStatusCountsDto":{"type":"object","properties":{"draft":{"type":"number"},"generating":{"type":"number"},"generated":{"type":"number"},"generation_failed":{"type":"number"},"in_review":{"type":"number"},"approved":{"type":"number"}},"required":["draft","generating","generated","generation_failed","in_review","approved"]},"DraftPathViewDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"isPublished":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"topicCount":{"type":"number"},"lessonCount":{"type":"number"},"totalEstimatedReadTimeInMinutes":{"type":"number"},"totalRecallQuestionsCount":{"type":"number"},"enrollmentStatus":{"type":"string","enum":["not_enrolled","active","completed"]},"lessonStatusCounts":{"$ref":"#/components/schemas/LessonStatusCountsDto"}},"required":["id","title","description","emoji","isPublished","createdAt","updatedAt","topicCount","lessonCount","totalEstimatedReadTimeInMinutes","totalRecallQuestionsCount","enrollmentStatus","lessonStatusCounts"]},"LessonSummaryDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"lessonIndex":{"type":"number"},"estimatedReadTimeInMinutes":{"type":"number"},"isCompleted":{"type":"boolean"}},"required":["id","title","lessonIndex","estimatedReadTimeInMinutes","isCompleted"]},"TopicWithLessonsDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"topicIndex":{"type":"number"},"enrollmentStatus":{"type":"string","enum":["not_enrolled","active","completed"]},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/LessonSummaryDto"}}},"required":["id","title","description","emoji","topicIndex","enrollmentStatus","lessons"]},"PathDetailDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"isPublished":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"topicCount":{"type":"number"},"lessonCount":{"type":"number"},"totalEstimatedReadTimeInMinutes":{"type":"number"},"totalRecallQuestionsCount":{"type":"number"},"enrollmentStatus":{"type":"string","enum":["not_enrolled","active","completed"]},"topics":{"type":"array","items":{"$ref":"#/components/schemas/TopicWithLessonsDto"}}},"required":["id","title","description","emoji","isPublished","createdAt","updatedAt","topicCount","lessonCount","totalEstimatedReadTimeInMinutes","totalRecallQuestionsCount","enrollmentStatus","topics"]},"UpdatePathDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"}}},"CreateTopicDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"pathId":{"type":"string"},"topicIndex":{"type":"number"}},"required":["title","description","emoji"]},"TopicViewDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"topicIndex":{"type":"number","nullable":true},"pathId":{"type":"string","nullable":true},"pathTitle":{"type":"string","nullable":true},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"lessonCount":{"type":"number"},"totalEstimatedReadTimeInMinutes":{"type":"number"},"totalRecallQuestionsCount":{"type":"number"},"enrollmentStatus":{"type":"string","enum":["not_enrolled","active","completed"]},"parentPathEnrollmentStatus":{"type":"string","enum":["not_enrolled","active","completed"],"nullable":true}},"required":["id","title","description","emoji","topicIndex","pathId","pathTitle","createdAt","updatedAt","lessonCount","totalEstimatedReadTimeInMinutes","totalRecallQuestionsCount","enrollmentStatus","parentPathEnrollmentStatus"]},"TopicDetailDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"topicIndex":{"type":"number","nullable":true},"pathId":{"type":"string","nullable":true},"pathTitle":{"type":"string","nullable":true},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"lessonCount":{"type":"number"},"totalEstimatedReadTimeInMinutes":{"type":"number"},"totalRecallQuestionsCount":{"type":"number"},"enrollmentStatus":{"type":"string","enum":["not_enrolled","active","completed"]},"parentPathEnrollmentStatus":{"type":"string","enum":["not_enrolled","active","completed"],"nullable":true},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/LessonSummaryDto"}}},"required":["id","title","description","emoji","topicIndex","pathId","pathTitle","createdAt","updatedAt","lessonCount","totalEstimatedReadTimeInMinutes","totalRecallQuestionsCount","enrollmentStatus","parentPathEnrollmentStatus","lessons"]},"UpdateTopicDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"pathId":{"type":"string"},"topicIndex":{"type":"number"}}},"LessonContentBlockDto":{"type":"object","properties":{"type":{"type":"string","enum":["paragraph","heading","code","image","callout","video"]},"content":{"type":"string"},"url":{"type":"string","description":"Image source URL (image blocks)"},"alt":{"type":"string","description":"Alt text for accessibility (image blocks)"},"caption":{"type":"string","description":"Caption shown beneath the image (image blocks)"},"variant":{"type":"string","enum":["note","tip","warning","mistake","info","success"],"description":"Visual variant for the callout (callout blocks)"}},"required":["type"]},"CreateLessonDto":{"type":"object","properties":{"topicId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"content":{"type":"array","items":{"$ref":"#/components/schemas/LessonContentBlockDto"}},"lessonIndex":{"type":"number"},"estimatedReadTimeInMinutes":{"type":"number"}},"required":["topicId","title","description","content","lessonIndex","estimatedReadTimeInMinutes"]},"LessonDetailDto":{"type":"object","properties":{"id":{"type":"string"},"topicId":{"type":"string"},"topicName":{"type":"string"},"pathId":{"type":"string","nullable":true},"pathName":{"type":"string","nullable":true},"title":{"type":"string"},"description":{"type":"string"},"content":{"type":"array","items":{"$ref":"#/components/schemas/LessonContentBlockDto"}},"lessonIndex":{"type":"number"},"estimatedReadTimeInMinutes":{"type":"number"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"totalLessonsInContext":{"type":"number"},"totalRecallQuestions":{"type":"number"},"completionStatus":{"type":"string","enum":["notStarted","inProgress","questions","complete"]},"readProgressPct":{"type":"number"},"currentQuestionIndex":{"type":"number","nullable":true}},"required":["id","topicId","topicName","pathId","pathName","title","description","content","lessonIndex","estimatedReadTimeInMinutes","createdAt","updatedAt","totalLessonsInContext","totalRecallQuestions","completionStatus","readProgressPct","currentQuestionIndex"]},"ComprehensionQuestionAnswerDto":{"type":"object","properties":{"id":{"type":"string"},"answer":{"type":"string"},"isCorrect":{"type":"boolean"}},"required":["id","answer","isCorrect"]},"ComprehensionQuestionDto":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answerExplanation":{"type":"string"},"answers":{"type":"array","items":{"$ref":"#/components/schemas/ComprehensionQuestionAnswerDto"}}},"required":["id","question","answerExplanation","answers"]},"CreateComprehensionQuestionAnswerDto":{"type":"object","properties":{"answer":{"type":"string"},"isCorrect":{"type":"boolean"}},"required":["answer","isCorrect"]},"CreateComprehensionQuestionDto":{"type":"object","properties":{"question":{"type":"string"},"answerExplanation":{"type":"string"},"answers":{"type":"array","items":{"$ref":"#/components/schemas/CreateComprehensionQuestionAnswerDto"}}},"required":["question","answerExplanation","answers"]},"CreateComprehensionQuestionsDto":{"type":"object","properties":{"questions":{"type":"array","items":{"$ref":"#/components/schemas/CreateComprehensionQuestionDto"}}},"required":["questions"]},"RecallQuestionDto":{"type":"object","properties":{"id":{"type":"string"},"lessonId":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"insight":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","lessonId","question","answer","insight","createdAt","updatedAt"]},"CreateRecallQuestionDto":{"type":"object","properties":{"question":{"type":"string"},"answer":{"type":"string"},"insight":{"type":"string"}},"required":["question","answer","insight"]},"CreateRecallQuestionsDto":{"type":"object","properties":{"questions":{"type":"array","items":{"$ref":"#/components/schemas/CreateRecallQuestionDto"}}},"required":["questions"]},"CreateComprehensionQuestionAttemptDto":{"type":"object","properties":{"comprehensionQuestionAnswerId":{"type":"string","description":"The answer the user selected"}},"required":["comprehensionQuestionAnswerId"]},"ComprehensionQuestionAttempt":{"type":"object","properties":{}},"UpdateLessonStatusDto":{"type":"object","properties":{"readProgressPct":{"type":"number","description":"Reading progress percentage (0–100)"},"status":{"type":"string","enum":["inProgress","questions"]},"currentQuestionIndex":{"type":"number","description":"Index of the current comprehension question (0-based)"}}},"LessonStatusDto":{"type":"object","properties":{"status":{"type":"string","enum":["inProgress","questions","complete"]},"readProgressPct":{"type":"number"},"completedAt":{"format":"date-time","type":"string","nullable":true},"currentQuestionIndex":{"type":"number","nullable":true}},"required":["status","readProgressPct","completedAt","currentQuestionIndex"]},"UserActivityDto":{"type":"object","properties":{"isFirstLessonCompleteTrigger":{"type":"boolean"}},"required":["isFirstLessonCompleteTrigger"]},"LessonCompleteResponseDto":{"type":"object","properties":{"status":{"type":"string","enum":["inProgress","questions","complete"]},"readProgressPct":{"type":"number"},"completedAt":{"format":"date-time","type":"string","nullable":true},"currentQuestionIndex":{"type":"number","nullable":true},"activityResult":{"$ref":"#/components/schemas/UserActivityDto"}},"required":["status","readProgressPct","completedAt","currentQuestionIndex","activityResult"]},"UpdateLessonDto":{"type":"object","properties":{"topicId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"content":{"type":"array","items":{"$ref":"#/components/schemas/LessonContentBlockDto"}},"lessonIndex":{"type":"number"},"estimatedReadTimeInMinutes":{"type":"number"}}},"DueRecallCardDto":{"type":"object","properties":{"userRecallCardId":{"type":"string"},"userId":{"type":"string"},"recallQuestionId":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"insight":{"type":"string"},"topicId":{"type":"string"},"pathId":{"type":"string","nullable":true,"description":"Parent path id when the topic belongs to a path; null for standalone topics. Lets clients theme the recall card using the same identifier as the topic/lesson screens."},"topicName":{"type":"string"},"topicEmoji":{"type":"string"}},"required":["userRecallCardId","userId","recallQuestionId","question","answer","insight","topicId","pathId","topicName","topicEmoji"]},"SubmitRecallReviewDto":{"type":"object","properties":{"userRecallCardId":{"type":"string"},"rating":{"type":"string","enum":["bad","almost","good"]}},"required":["userRecallCardId","rating"]},"RecallReviewResultDto":{"type":"object","properties":{"newInterval":{"type":"number","description":"Days until this card is due for review again"},"activityResult":{"$ref":"#/components/schemas/UserActivityDto"}},"required":["newInterval","activityResult"]},"DailyScheduleViewDto":{"type":"object","properties":{"enrollmentId":{"type":"string"},"subjectType":{"type":"string","enum":["path","topic"]},"subjectId":{"type":"string"},"subjectTitle":{"type":"string"},"subjectEmoji":{"type":"string"},"contextTitle":{"type":"string","nullable":true},"contextId":{"type":"string","nullable":true,"description":"Parent path id when the lesson belongs to a topic that has a parent path; null otherwise. Lets clients theme the schedule entry using the same identifier as the path/topic/lesson screens."},"scheduleStatus":{"type":"string","enum":["pending","completed"]},"lessonId":{"type":"string"},"lessonTitle":{"type":"string"},"estimatedReadTimeInMinutes":{"type":"number"},"completionStatus":{"type":"string","enum":["notStarted","inProgress","questions","complete"]},"readProgressPct":{"type":"number"},"currentQuestionIndex":{"type":"number","nullable":true},"totalSubjectLessons":{"type":"number"},"totalSubjectLessonsCompleted":{"type":"number"}},"required":["enrollmentId","subjectType","subjectId","subjectTitle","subjectEmoji","contextTitle","contextId","scheduleStatus","lessonId","lessonTitle","estimatedReadTimeInMinutes","completionStatus","readProgressPct","currentQuestionIndex","totalSubjectLessons","totalSubjectLessonsCompleted"]},"CreateEnrollmentDto":{"type":"object","properties":{"topicId":{"type":"string","description":"Topic ID to enroll in"},"pathId":{"type":"string","description":"Path ID to enroll in"}}},"UserEnrollment":{"type":"object","properties":{}},"EnrollmentResponseDto":{"type":"object","properties":{"id":{"type":"string"},"subjectType":{"type":"string","enum":["path","topic"]},"subjectId":{"type":"string"},"subjectTitle":{"type":"string"},"subjectEmoji":{"type":"string"},"contextTitle":{"type":"string","nullable":true,"description":"Parent path title for topic enrollments; null for path enrollments"},"contextId":{"type":"string","nullable":true,"description":"Parent path id for topic enrollments; null for path enrollments. Lets clients theme a topic enrollment using the same identifier as the path it belongs to."},"status":{"type":"string","enum":["active","completed"]},"totalLessons":{"type":"number"},"lessonsCompleted":{"type":"number"},"enrolledAt":{"format":"date-time","type":"string"}},"required":["id","subjectType","subjectId","subjectTitle","subjectEmoji","contextTitle","contextId","status","totalLessons","lessonsCompleted","enrolledAt"]},"DiscoverItemDto":{"type":"object","properties":{"type":{"type":"string","enum":["path"]},"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"enrollmentCount":{"type":"number"},"createdAt":{"format":"date-time","type":"string"}},"required":["type","id","title","description","emoji","enrollmentCount","createdAt"]},"DiscoverResponseDto":{"type":"object","properties":{"new":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/DiscoverItemDto"}]},"trending":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/DiscoverItemDto"}]},"popular":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/DiscoverItemDto"}]}}},"GenerationHintsDto":{"type":"object","properties":{"topicCount":{"type":"number","description":"Override auto topic count (min 3)"},"lessonsPerTopic":{"type":"number","description":"Override auto lessons per topic (min 3)"},"topicTitles":{"description":"Seed specific topic titles","type":"array","items":{"type":"string"}}}},"GenerateOutlineRequestDto":{"type":"object","properties":{"prompt":{"type":"string","description":"Plain-English instruction (e.g. \"A 3-topic path on SQL fundamentals\")"},"type":{"type":"string","enum":["path","topic"],"description":"\"path\" generates a full path; \"topic\" adds to an existing path"},"pathId":{"type":"string","description":"Required when type = topic — provides path context"},"hints":{"$ref":"#/components/schemas/GenerationHintsDto"}},"required":["prompt","type"]},"LessonOutlineDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"}},"required":["title","description"]},"TopicOutlineDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/LessonOutlineDto"}}},"required":["title","description","emoji","lessons"]},"OutlineDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"topics":{"type":"array","items":{"$ref":"#/components/schemas/TopicOutlineDto"}}},"required":["title","description","emoji","topics"]},"GenerateOutlineResponseDto":{"type":"object","properties":{"outline":{"$ref":"#/components/schemas/OutlineDto"}},"required":["outline"]},"SuggestPathsRequestDto":{"type":"object","properties":{"field":{"type":"string","description":"The field / domain to suggest paths for (e.g. \"Software Engineering\")","example":"Software Engineering"}},"required":["field"]},"PathSuggestionDto":{"type":"object","properties":{"title":{"type":"string","description":"Suggested path title","example":"Distributed Systems Fundamentals"},"description":{"type":"string","description":"One-line description of the path","example":"Learn the core principles behind building reliable, scalable distributed systems"},"emoji":{"type":"string","description":"Suggested emoji for the path","example":"🌐"}},"required":["title","description","emoji"]},"SuggestPathsResponseDto":{"type":"object","properties":{"suggestions":{"description":"List of suggested learning paths","type":"array","items":{"$ref":"#/components/schemas/PathSuggestionDto"}}},"required":["suggestions"]},"GenerateContentRequestDto":{"type":"object","properties":{"type":{"type":"string","enum":["path","topic"]},"pathId":{"type":"string","description":"Required when type = topic"},"outline":{"$ref":"#/components/schemas/OutlineDto"}},"required":["type","outline"]},"GeneratedLessonDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"estimatedReadTimeInMinutes":{"type":"number"},"content":{"type":"array","items":{"$ref":"#/components/schemas/LessonContentBlockDto"}},"comprehensionQuestions":{"type":"array","items":{"$ref":"#/components/schemas/CreateComprehensionQuestionDto"}},"recallQuestions":{"type":"array","items":{"$ref":"#/components/schemas/CreateRecallQuestionDto"}}},"required":["title","description","estimatedReadTimeInMinutes","content","comprehensionQuestions","recallQuestions"]},"GeneratedTopicDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedLessonDto"}}},"required":["title","description","emoji","lessons"]},"GeneratedPathDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"topics":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedTopicDto"}}},"required":["title","description","emoji","topics"]},"GenerateContentResponseDto":{"type":"object","properties":{"type":{"type":"string","enum":["path","topic"]},"data":{"$ref":"#/components/schemas/GeneratedPathDto"}},"required":["type","data"]},"SaveOutlineDraftRequestDto":{"type":"object","properties":{"type":{"type":"string","enum":["path","topic"]},"pathId":{"type":"string","description":"Required when type = topic"},"outline":{"$ref":"#/components/schemas/OutlineDto"}},"required":["type","outline"]},"SavedTopicSummaryDto":{"type":"object","properties":{"topicId":{"type":"string"},"title":{"type":"string"},"lessonCount":{"type":"number"}},"required":["topicId","title","lessonCount"]},"SaveOutlineDraftResponseDto":{"type":"object","properties":{"pathId":{"type":"string"},"topics":{"type":"array","items":{"$ref":"#/components/schemas/SavedTopicSummaryDto"}}},"required":["pathId","topics"]},"GenerateLessonContentRequestDto":{"type":"object","properties":{"lessonId":{"type":"string","description":"ID of the lesson to generate content for"}},"required":["lessonId"]},"GenerateLessonsBatchRequestDto":{"type":"object","properties":{"lessonIds":{"description":"IDs of the lessons to generate content for","type":"array","items":{"type":"string"}}},"required":["lessonIds"]},"BatchGenerationResultItemDto":{"type":"object","properties":{"lessonId":{"type":"string"},"success":{"type":"boolean"},"error":{"type":"string"}},"required":["lessonId","success"]},"GenerateLessonsBatchResponseDto":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/BatchGenerationResultItemDto"}}},"required":["results"]},"SelectionDto":{"type":"object","properties":{"startOffset":{"type":"number","description":"Start character offset within block content"},"endOffset":{"type":"number","description":"End character offset within block content"},"selectedText":{"type":"string","description":"The selected text (for verification)"}},"required":["startOffset","endOffset","selectedText"]},"RegenerateContentBlockRequestDto":{"type":"object","properties":{"lessonId":{"type":"string","description":"ID of the lesson containing the block"},"blockIndex":{"type":"number","description":"Index into lesson.content[] to regenerate"},"prompt":{"type":"string","description":"Optional instruction for regeneration"},"selection":{"description":"Selection within the block to regenerate. Omit to regenerate the entire block.","allOf":[{"$ref":"#/components/schemas/SelectionDto"}]}},"required":["lessonId","blockIndex"]},"SuggestLessonsRequestDto":{"type":"object","properties":{"count":{"type":"number","description":"Number of lessons to suggest for the topic","minimum":1,"maximum":20,"example":5}},"required":["count"]},"SuggestedLessonDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"lessonIndex":{"type":"number"}},"required":["id","title","description","lessonIndex"]},"SuggestLessonsResponseDto":{"type":"object","properties":{"topicId":{"type":"string"},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/SuggestedLessonDto"}}},"required":["topicId","lessons"]},"PathExportPathDto":{"type":"object","properties":{"pathId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"}},"required":["title","description","emoji"]},"PathExportAnswerDto":{"type":"object","properties":{"answer":{"type":"string"},"isCorrect":{"type":"boolean"}},"required":["answer","isCorrect"]},"PathExportComprehensionQuestionDto":{"type":"object","properties":{"question":{"type":"string"},"answerExplanation":{"type":"string"},"answers":{"type":"array","items":{"$ref":"#/components/schemas/PathExportAnswerDto"}}},"required":["question","answerExplanation","answers"]},"PathExportRecallQuestionDto":{"type":"object","properties":{"question":{"type":"string"},"answer":{"type":"string"},"insight":{"type":"string"},"comprehensionQuestionIndex":{"type":"number","description":"Array position (0-based) of the linked comprehension question within this lesson, or null if unlinked. Used to preserve the recall↔comprehension link across UUID regeneration on import.","nullable":true}},"required":["question","answer","insight","comprehensionQuestionIndex"]},"PathExportLessonDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"lessonIndex":{"type":"number"},"estimatedReadTimeInMinutes":{"type":"number"},"content":{"type":"array","items":{"$ref":"#/components/schemas/LessonContentBlockDto"}},"reviewStatus":{"type":"string","enum":["draft","generating","generated","generation_failed","in_review","approved"]},"comprehensionQuestions":{"type":"array","items":{"$ref":"#/components/schemas/PathExportComprehensionQuestionDto"}},"recallQuestions":{"type":"array","items":{"$ref":"#/components/schemas/PathExportRecallQuestionDto"}}},"required":["title","description","lessonIndex","estimatedReadTimeInMinutes","content","reviewStatus","comprehensionQuestions","recallQuestions"]},"PathExportTopicDto":{"type":"object","properties":{"topicIndex":{"type":"number"},"title":{"type":"string"},"description":{"type":"string"},"emoji":{"type":"string"},"lessons":{"type":"array","items":{"$ref":"#/components/schemas/PathExportLessonDto"}}},"required":["topicIndex","title","description","emoji","lessons"]},"PathExportDto":{"type":"object","properties":{"version":{"type":"string","description":"Export format version","enum":["1.0"]},"exportedAt":{"type":"string","description":"ISO-8601 timestamp when export was produced"},"path":{"$ref":"#/components/schemas/PathExportPathDto"},"topics":{"type":"array","items":{"$ref":"#/components/schemas/PathExportTopicDto"}}},"required":["version","exportedAt","path","topics"]},"ImportPathResponseDto":{"type":"object","properties":{"pathId":{"type":"string","description":"ID of the imported path. Either a freshly generated UUID or the existing path's UUID if the import matched on path.pathId."},"operation":{"type":"string","description":"Whether the import created a new path or replaced the contents of an existing one (matched by path.pathId in the export).","enum":["created","updated"]}},"required":["pathId","operation"]},"LessonReviewResponseDto":{"type":"object","properties":{"id":{"type":"string"},"lessonId":{"type":"string"},"status":{"type":"string","enum":["draft","generating","generated","generation_failed","in_review","approved"]},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","lessonId","status","createdAt","updatedAt"]},"UpdateReviewStatusRequestDto":{"type":"object","properties":{"status":{"type":"string","enum":["draft","generating","generated","generation_failed","in_review","approved"]}},"required":["status"]},"LessonReviewCommentResponseDto":{"type":"object","properties":{"id":{"type":"string"},"lessonId":{"type":"string"},"contentBlockIndex":{"type":"number"},"authorId":{"type":"string"},"authorName":{"type":"string"},"text":{"type":"string"},"selectionStartOffset":{"type":"number","nullable":true},"selectionEndOffset":{"type":"number","nullable":true},"selectedText":{"type":"string","nullable":true},"parentId":{"type":"string","nullable":true},"replies":{"type":"array","items":{"$ref":"#/components/schemas/LessonReviewCommentResponseDto"}},"isResolved":{"type":"boolean"},"resolvedByName":{"type":"string"},"resolvedAt":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","lessonId","contentBlockIndex","authorId","authorName","text","isResolved","createdAt","updatedAt"]},"CreateLessonCommentRequestDto":{"type":"object","properties":{"contentBlockIndex":{"type":"number"},"text":{"type":"string"},"selectionStartOffset":{"type":"number","description":"Start offset within block text (for selection-level comments)"},"selectionEndOffset":{"type":"number","description":"End offset within block text (for selection-level comments)"},"selectedText":{"type":"string","description":"The selected text (for display when offsets become stale)"},"parentId":{"type":"string","description":"Parent comment ID for replies (one level deep)"}},"required":["contentBlockIndex","text"]},"UpdateLessonCommentRequestDto":{"type":"object","properties":{"text":{"type":"string"},"isResolved":{"type":"boolean"}}},"TrackEventDto":{"type":"object","properties":{"eventName":{"type":"string","example":"page_viewed"},"properties":{"type":"object","example":{"path":"/dashboard"}},"sessionId":{"type":"string","format":"uuid"},"occurredAt":{"type":"string","example":"2026-04-06T10:00:00.000Z","description":"ISO 8601 timestamp of when the event occurred on the client"}},"required":["eventName"]},"BatchTrackEventsDto":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/TrackEventDto"}}},"required":["events"]},"UserAnalyticsSortBy":{"type":"string","enum":["engagementScore","lastActiveAt","lessonsCompleted","cardsRecalled","cardsMastered","currentDaysStreak","activeDaysLast14d"]},"SortDirection":{"type":"string","enum":["asc","desc"]},"UserAnalyticsRowDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"avatarUrl":{"type":"string","nullable":true},"role":{"type":"string","enum":["user","admin"]},"createdAt":{"type":"string"},"onboardingCompleted":{"type":"boolean"},"lessonsCompleted":{"type":"number"},"topicsCompleted":{"type":"number"},"cardsRecalled":{"type":"number"},"cardsMastered":{"type":"number"},"currentDaysStreak":{"type":"number"},"bestStreak":{"type":"number"},"activeDaysLast14d":{"type":"number","description":"Days the user logged in across the last 14 calendar days."},"lastActiveAt":{"type":"string","nullable":true,"description":"ISO timestamp of the most recent user_event, or null if the user has never produced one."},"engagementScore":{"type":"number","description":"Composite engagement score used to rank the top users list. Formula is fixed; not user-tunable."}},"required":["id","name","email","avatarUrl","role","createdAt","onboardingCompleted","lessonsCompleted","topicsCompleted","cardsRecalled","cardsMastered","currentDaysStreak","bestStreak","activeDaysLast14d","lastActiveAt","engagementScore"]},"TopUsersResponseDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/UserAnalyticsRowDto"}},"total":{"type":"number"}},"required":["items","total"]},"UserJourneyHeaderDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"avatarUrl":{"type":"string","nullable":true},"role":{"type":"string","enum":["user","admin"]},"domain":{"type":"string","nullable":true},"experience":{"type":"string","nullable":true},"onboardingCompleted":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","name","email","avatarUrl","role","domain","experience","onboardingCompleted","createdAt"]},"UserJourneyStatsDto":{"type":"object","properties":{"lessonsCompleted":{"type":"number"},"topicsCompleted":{"type":"number"},"cardsRecalled":{"type":"number"},"cardsMastered":{"type":"number"},"currentDaysStreak":{"type":"number"},"bestStreak":{"type":"number"},"activeDaysLast14d":{"type":"number"},"lastActiveAt":{"type":"string","nullable":true}},"required":["lessonsCompleted","topicsCompleted","cardsRecalled","cardsMastered","currentDaysStreak","bestStreak","activeDaysLast14d","lastActiveAt"]},"UserJourneyEnrollmentDto":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["path","topic"]},"refId":{"type":"string"},"title":{"type":"string"},"emoji":{"type":"string"},"status":{"type":"string","enum":["active","completed"]},"enrolledAt":{"type":"string"},"completedAt":{"type":"string","nullable":true},"totalLessons":{"type":"number"},"completedLessons":{"type":"number"}},"required":["id","type","refId","title","emoji","status","enrolledAt","completedAt","totalLessons","completedLessons"]},"UserJourneyActivityDayDto":{"type":"object","properties":{"day":{"type":"string","description":"ISO date (YYYY-MM-DD)"},"minutesSpentInApp":{"type":"number"},"lessonsFinished":{"type":"number"},"recallQuestionsFinished":{"type":"number"},"hasLoggedIn":{"type":"boolean"}},"required":["day","minutesSpentInApp","lessonsFinished","recallQuestionsFinished","hasLoggedIn"]},"UserJourneyFunnelStepDto":{"type":"object","properties":{"step":{"type":"string","enum":["signed_up","first_enrollment","first_lesson_started","first_lesson_completed","first_recall_rated","first_mastery"]},"reachedAt":{"type":"string","nullable":true,"description":"ISO timestamp when the user first reached this step, or null if not yet reached."}},"required":["step","reachedAt"]},"UserJourneySessionStatsDto":{"type":"object","properties":{"totalSessions":{"type":"number","description":"Count of distinct session_id values seen across all of the user's events."},"totalEvents":{"type":"number"},"medianMinutes":{"type":"number","description":"Median session duration in minutes (max - min occurred_at per session). 0 when there are no multi-event sessions."},"mostRecentSessionAt":{"type":"string","nullable":true}},"required":["totalSessions","totalEvents","medianMinutes","mostRecentSessionAt"]},"UserJourneyRecallBucketDto":{"type":"object","properties":{"label":{"type":"string","description":"Human label for the SR interval bucket, e.g. '1d', '4-7d', '31d+'."},"count":{"type":"number"}},"required":["label","count"]},"UserJourneyMilestoneDto":{"type":"object","properties":{"type":{"type":"string","enum":["enrollment","lesson_completed","mastery"]},"occurredAt":{"type":"string"},"label":{"type":"string"},"refId":{"type":"string"}},"required":["type","occurredAt","label","refId"]},"UserJourneyEventDto":{"type":"object","properties":{"id":{"type":"string"},"eventName":{"type":"string"},"properties":{"type":"object","nullable":true,"additionalProperties":true},"occurredAt":{"type":"string"},"sessionId":{"type":"string","nullable":true}},"required":["id","eventName","properties","occurredAt","sessionId"]},"UserJourneyDto":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/UserJourneyHeaderDto"},"stats":{"$ref":"#/components/schemas/UserJourneyStatsDto"},"enrollments":{"type":"array","items":{"$ref":"#/components/schemas/UserJourneyEnrollmentDto"}},"activity30d":{"description":"Last 30 days of daily activity, oldest first. Missing days are zero-filled so callers can render a fixed-width heatmap.","type":"array","items":{"$ref":"#/components/schemas/UserJourneyActivityDayDto"}},"funnel":{"description":"Activation funnel for this user. Always returns all six steps in canonical order; reachedAt is null for steps not yet reached.","type":"array","items":{"$ref":"#/components/schemas/UserJourneyFunnelStepDto"}},"sessionStats":{"$ref":"#/components/schemas/UserJourneySessionStatsDto"},"recallBuckets":{"description":"Counts of user_recall_cards bucketed by their current SR interval.","type":"array","items":{"$ref":"#/components/schemas/UserJourneyRecallBucketDto"}},"milestones":{"type":"array","items":{"$ref":"#/components/schemas/UserJourneyMilestoneDto"}},"rawEvents":{"type":"array","items":{"$ref":"#/components/schemas/UserJourneyEventDto"}},"nextCursor":{"type":"string","nullable":true,"description":"Pass back as the `before` query param to fetch the next page of raw events. Null when no more events exist."}},"required":["user","stats","enrollments","activity30d","funnel","sessionStats","recallBuckets","milestones","rawEvents","nextCursor"]},"MarketingFunnelRange":{"type":"string","enum":["7d","30d","90d","all"]},"MarketingFunnelTotalsDto":{"type":"object","properties":{"leads":{"type":"number"},"converted":{"type":"number"},"conversionRate":{"type":"number","description":"Conversion rate in [0, 1]. 0 when leads = 0."},"uniqueEmails":{"type":"number","description":"Distinct lead emails in the window."}},"required":["leads","converted","conversionRate","uniqueEmails"]},"MarketingFunnelPreviousTotalsDto":{"type":"object","properties":{"leads":{"type":"number"},"converted":{"type":"number"},"conversionRate":{"type":"number"}},"required":["leads","converted","conversionRate"]},"MarketingFunnelBySourceDto":{"type":"object","properties":{"source":{"type":"string"},"leads":{"type":"number"},"converted":{"type":"number"},"conversionRate":{"type":"number"}},"required":["source","leads","converted","conversionRate"]},"MarketingFunnelSummaryDto":{"type":"object","properties":{"range":{"$ref":"#/components/schemas/MarketingFunnelRange"},"source":{"type":"string","nullable":true},"windowStart":{"type":"string","description":"Inclusive ISO start of the active window."},"windowEnd":{"type":"string","description":"Exclusive ISO end of the active window."},"totals":{"$ref":"#/components/schemas/MarketingFunnelTotalsDto"},"previous":{"description":"Same-length window immediately before windowStart.","allOf":[{"$ref":"#/components/schemas/MarketingFunnelPreviousTotalsDto"}]},"bySource":{"type":"array","items":{"$ref":"#/components/schemas/MarketingFunnelBySourceDto"}}},"required":["range","source","windowStart","windowEnd","totals","previous","bySource"]},"MarketingFunnelBucket":{"type":"string","enum":["day","week"]},"MarketingFunnelTimeseriesPointDto":{"type":"object","properties":{"bucket":{"type":"string","description":"ISO timestamp at the start of the bucket."},"leads":{"type":"number"},"converted":{"type":"number"}},"required":["bucket","leads","converted"]},"MarketingFunnelTimeseriesDto":{"type":"object","properties":{"range":{"$ref":"#/components/schemas/MarketingFunnelRange"},"bucket":{"$ref":"#/components/schemas/MarketingFunnelBucket"},"source":{"type":"string","nullable":true},"points":{"type":"array","items":{"$ref":"#/components/schemas/MarketingFunnelTimeseriesPointDto"}}},"required":["range","bucket","source","points"]},"MarketingFunnelConvertedFilter":{"type":"string","enum":["true","false","all"]},"MarketingFunnelSortBy":{"type":"string","enum":["createdAt","convertedAt","daysToConvert"]},"MarketingFunnelLeadRowDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string","nullable":true},"source":{"type":"string"},"createdAt":{"type":"string"},"convertedAt":{"type":"string","nullable":true,"description":"ISO timestamp the matching user signed up, or null if the lead has not converted."},"daysToConvert":{"type":"number","nullable":true,"description":"Whole days between lead capture and signup, or null."},"userId":{"type":"string","nullable":true}},"required":["id","email","name","source","createdAt","convertedAt","daysToConvert","userId"]},"MarketingFunnelLeadsListDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/MarketingFunnelLeadRowDto"}},"total":{"type":"number"}},"required":["items","total"]},"MarketingFunnelSourcesDto":{"type":"object","properties":{"sources":{"type":"array","items":{"type":"string"}}},"required":["sources"]},"SendWelcomeDto":{"type":"object","properties":{"email":{"type":"string","description":"Recipient email address."},"name":{"type":"string","description":"Name shown in the welcome greeting. The heading and greeting drop the name when omitted."}},"required":["email"]},"SendEmailResponseDto":{"type":"object","properties":{"messageId":{"type":"string","nullable":true,"description":"Resend message id, or null if sending was skipped/failed."}},"required":["messageId"]},"SendBetaInviteDto":{"type":"object","properties":{"email":{"type":"string","description":"Recipient email address."},"name":{"type":"string","description":"Recipient's name. Personalizes the greeting (e.g. 'Hey Sara,') when provided."}},"required":["email"]},"RegisterPushTokenDto":{"type":"object","properties":{"nativeToken":{"type":"string","example":"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]"},"platform":{"type":"string","enum":["ios","android","web"]},"timezone":{"type":"string","example":"America/Los_Angeles"},"appVersion":{"type":"string","example":"1.0.0"},"deviceName":{"type":"string","example":"iPhone 15 Pro"}},"required":["nativeToken","platform"]},"PushTokenResponseDto":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string"},"timezone":{"type":"string","nullable":true},"deviceName":{"type":"string","nullable":true},"lastSeenAt":{"type":"string"},"disabled":{"type":"boolean"}},"required":["id","platform","lastSeenAt","disabled"]},"PushNotificationPrefDto":{"type":"object","properties":{"category":{"type":"string","enum":["daily.reminder","streak.protect","winback","path.published"]},"enabled":{"type":"boolean"},"preferredHourLocal":{"type":"number"},"timezone":{"type":"string","nullable":true}},"required":["category","enabled","preferredHourLocal"]},"PushNotificationPrefListDto":{"type":"object","properties":{"prefs":{"type":"array","items":{"$ref":"#/components/schemas/PushNotificationPrefDto"}},"pushNotificationsEnabled":{"type":"boolean"}},"required":["prefs","pushNotificationsEnabled"]},"UpdatePushNotificationPrefDto":{"type":"object","properties":{"category":{"type":"string","enum":["daily.reminder","streak.protect","winback","path.published"]},"enabled":{"type":"boolean"},"preferredHourLocal":{"type":"number","minimum":0,"maximum":23},"timezone":{"type":"string"}},"required":["category"]},"SetPushNotificationsEnabledDto":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]},"PushNotificationTestPayloadDto":{"type":"object","properties":{"title":{"type":"string"},"body":{"type":"string"},"data":{"type":"object"}},"required":["title","body"]},"PushNotificationTestDto":{"type":"object","properties":{"userId":{"type":"string"},"category":{"type":"string","enum":["daily.reminder","streak.protect","winback","path.published"]},"payload":{"$ref":"#/components/schemas/PushNotificationTestPayloadDto"}},"required":["userId","category"]},"PushNotificationTestResponseDto":{"type":"object","properties":{"logId":{"type":"string","nullable":true}},"required":["logId"]},"CreateMarketingLeadDto":{"type":"object","properties":{"name":{"type":"string","example":"Ada Lovelace"},"email":{"type":"string","example":"ada@example.com"},"source":{"type":"string","example":"landing_hero","description":"Where the lead came from (e.g. landing_hero, landing_waitlist, discord, referral)"}},"required":["email","source"]},"MarketingLeadResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","nullable":true},"email":{"type":"string"},"source":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"created":{"type":"boolean","description":"true if a new row was inserted"}},"required":["id","email","source","createdAt","created"]}}}}