{ "openapi": "3.0.3", "info": { "title": "CourseLit REST API", "version": "1.0.0", "description": "OpenAPI documentation for the public CourseLit REST API surface." }, "servers": [ { "url": "/", "description": "Current CourseLit school origin" } ], "tags": [ { "name": "Users", "description": "Create and update users programmatically with an API key." }, { "name": "Products", "description": "Create, read, update, and delete products through the public REST API." }, { "name": "Product Payment Plans", "description": "Manage payment plans for course and download products." }, { "name": "Product Content", "description": "Manage sections and lessons within a product." }, { "name": "Product Customers", "description": "Enroll customers and read enrollment/progress snapshots." }, { "name": "Product Learner Actions", "description": "Submit learner quiz evaluations and mark lessons complete." }, { "name": "Media Uploads", "description": "Generate MediaLit signatures for direct media uploads." } ], "paths": { "/api/user": { "post": { "tags": ["Users"], "summary": "Create a user", "operationId": "createUser", "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateUserRequest" }, "examples": { "basic": { "summary": "Create a subscribed user", "value": { "email": "student@example.com", "name": "Student Example", "permissions": ["community:manage"], "subscribedToUpdates": true } }, "legacy": { "summary": "Legacy body API key support", "value": { "apikey": "legacy-api-key", "email": "student@example.com" } } } } } }, "responses": { "200": { "description": "User created successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserMutationSuccess" }, "examples": { "success": { "value": { "email": "5d41402abc4b2a76b9719d911017c592" } } } } } }, "400": { "description": "Bad request or user already exists.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "badRequest": { "value": { "message": "Bad request" } }, "duplicate": { "value": { "error": "User already exists" } } } } } }, "401": { "description": "Invalid API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "unauthorized": { "value": { "message": "Unauthorized" } } } } } }, "404": { "description": "Domain not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "domainNotFound": { "value": { "message": "Domain not found" } } } } } }, "500": { "description": "Internal server error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "serverError": { "value": { "error": "Internal server error" } } } } } } } }, "patch": { "tags": ["Users"], "summary": "Update a user", "operationId": "updateUser", "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateUserRequest" }, "examples": { "updateName": { "summary": "Update user profile fields", "value": { "email": "student@example.com", "name": "Updated Student", "subscribedToUpdates": false } }, "legacy": { "summary": "Legacy body API key support", "value": { "apikey": "legacy-api-key", "email": "student@example.com", "permissions": ["community:comment"] } } } } } }, "responses": { "200": { "description": "User updated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserMutationSuccess" }, "examples": { "success": { "value": { "email": "5d41402abc4b2a76b9719d911017c592" } } } } } }, "400": { "description": "Bad request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "badRequest": { "value": { "message": "Bad request" } } } } } }, "401": { "description": "Invalid API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "unauthorized": { "value": { "message": "Unauthorized" } } } } } }, "404": { "description": "Domain or user not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "notFound": { "value": { "error": "User not found" } } } } } }, "500": { "description": "Internal server error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "serverError": { "value": { "error": "Internal server error" } } } } } } } } }, "/api/products": { "get": { "tags": ["Products"], "summary": "List products", "operationId": "listProducts", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "type", "in": "query", "schema": { "type": "string", "enum": ["course", "download", "blog"] } }, { "name": "published", "in": "query", "schema": { "type": "boolean" } }, { "name": "search", "in": "query", "schema": { "type": "string" } }, { "name": "tags", "in": "query", "description": "Filter products matching any of the supplied tags. Pass as `tags=ai,beginner`.", "style": "form", "explode": false, "schema": { "type": "array", "items": { "type": "string" } } }, { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1, "minimum": 1 } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "minimum": 1, "maximum": 200 } } ], "responses": { "200": { "description": "Products returned successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductListResponse" } } } }, "401": { "description": "Invalid API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "post": { "tags": ["Products"], "summary": "Create a draft product", "operationId": "createProduct", "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductCreateRequest" }, "example": { "title": "AI Foundations", "type": "course" } } } }, "responses": { "201": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Product" } } } }, "400": { "description": "Unsupported or invalid product field.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "422": { "description": "Product could not be created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}": { "get": { "tags": ["Products"], "summary": "Get a product", "operationId": "getProduct", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Product" } } } }, "404": { "description": "Product not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "patch": { "tags": ["Products"], "summary": "Update product metadata", "operationId": "updateProduct", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductUpdateRequest" }, "example": { "title": "AI Foundations", "slug": "ai-foundations", "description": "{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Updated course description.\"}]}]}", "published": false, "privacy": "unlisted", "tags": ["ai", "beginner"] } } } }, "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Product" } } } }, "400": { "description": "Malformed JSON body, non-object body, or unsupported product field.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "422": { "description": "Product could not be updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "delete": { "tags": ["Products"], "summary": "Delete a product", "operationId": "deleteProduct", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkResponse" } } } }, "422": { "description": "Product could not be deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/payment-plans": { "get": { "tags": ["Product Payment Plans"], "summary": "List product payment plans", "operationId": "listProductPaymentPlans", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentPlanListResponse" } } } }, "422": { "description": "Payment plans could not be fetched.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "post": { "tags": ["Product Payment Plans"], "summary": "Create a product payment plan", "operationId": "createProductPaymentPlan", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentPlanCreateRequest" }, "example": { "name": "Lifetime access", "type": "onetime", "oneTimeAmount": 9900, "description": "One-time payment for lifetime product access." } } } }, "responses": { "201": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentPlan" } } } }, "400": { "description": "Unsupported payment plan field.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "422": { "description": "Payment plan validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/payment-plans/{planId}": { "get": { "tags": ["Product Payment Plans"], "summary": "Get a product payment plan", "operationId": "getProductPaymentPlan", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "planId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentPlan" } } } }, "404": { "description": "Payment plan not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "patch": { "tags": ["Product Payment Plans"], "summary": "Update a product payment plan", "operationId": "updateProductPaymentPlan", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "planId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentPlanUpdateRequest" }, "example": { "name": "Updated lifetime access", "oneTimeAmount": 12900, "description": "Updated one-time payment plan." } } } }, "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentPlan" } } } }, "400": { "description": "Unsupported payment plan field.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "422": { "description": "Payment plan validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "delete": { "tags": ["Product Payment Plans"], "summary": "Archive a product payment plan", "operationId": "archiveProductPaymentPlan", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "planId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentPlan" } } } }, "422": { "description": "Payment plan could not be archived.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/payment-plans/{planId}/default": { "post": { "tags": ["Product Payment Plans"], "summary": "Set the default product payment plan", "operationId": "setDefaultProductPaymentPlan", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "planId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentPlan" } } } }, "422": { "description": "Default plan could not be changed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/sections": { "get": { "tags": ["Product Content"], "summary": "List product sections", "operationId": "listProductSections", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SectionListResponse" } } } }, "404": { "description": "Product not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "post": { "tags": ["Product Content"], "summary": "Create a product section", "operationId": "createProductSection", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SectionCreateRequest" }, "example": { "name": "Getting started" } } } }, "responses": { "201": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Section" } } } }, "422": { "description": "Section could not be created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/sections/{sectionId}": { "patch": { "tags": ["Product Content"], "summary": "Update a product section", "operationId": "updateProductSection", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "sectionId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SectionUpdateRequest" }, "example": { "name": "Updated getting started", "drip": { "status": true, "type": "relative-date", "delayInMillis": 2 } } } } }, "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Section" } } } }, "400": { "description": "Unsupported section field or invalid drip configuration.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "422": { "description": "Section could not be updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "delete": { "tags": ["Product Content"], "summary": "Delete a product section", "operationId": "deleteProductSection", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "sectionId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkResponse" } } } }, "422": { "description": "Section could not be deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/sections/reorder": { "post": { "tags": ["Product Content"], "summary": "Reorder product sections", "operationId": "reorderProductSections", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "sectionIds": { "type": "array", "items": { "type": "string" } } }, "required": ["sectionIds"] } } } }, "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkResponse" } } } }, "422": { "description": "Sections could not be reordered.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/lessons": { "get": { "tags": ["Product Content"], "summary": "List product lessons", "operationId": "listProductLessons", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LessonListResponse" } } } }, "404": { "description": "Product not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "post": { "tags": ["Product Content"], "summary": "Create a product lesson", "description": "Creates a lesson. `text` lessons accept Tiptap/ProseMirror JSON in `content`; `embed` lessons accept `{ value }` in `content`; `quiz` lessons accept quiz JSON in `content`; media-backed lessons (`video`, `audio`, `pdf`, `file`) use `media`. SCORM lessons are not supported.", "operationId": "createProductLesson", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LessonCreateRequest" }, "example": { "title": "Introduction to AI", "type": "text", "groupId": "section_abc123", "content": { "type": "doc", "content": [ { "type": "heading", "attrs": { "level": 2 }, "content": [ { "type": "text", "text": "Install Rust" } ] }, { "type": "paragraph", "content": [ { "type": "text", "text": "Install " }, { "type": "text", "marks": [ { "type": "bold" } ], "text": "rustup" }, { "type": "text", "text": " and create your first project." } ] }, { "type": "bulletList", "content": [ { "type": "listItem", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Run the installer." } ] } ] } ] }, { "type": "codeBlock", "attrs": { "language": "bash" }, "content": [ { "type": "text", "text": "cargo new hello-rust" } ] } ] }, "requiresEnrollment": true, "published": false } } } }, "responses": { "201": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Lesson" } } } }, "422": { "description": "SCORM lessons are not supported, or lesson validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/lessons/{lessonId}": { "get": { "tags": ["Product Content"], "summary": "Get a product lesson", "operationId": "getProductLesson", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "lessonId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Lesson" } } } }, "404": { "description": "Lesson not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "patch": { "tags": ["Product Content"], "summary": "Update a product lesson", "description": "Updates editable lesson fields. Lesson type and section cannot be changed after creation. Use the same content/media shapes documented on create. SCORM lesson updates are rejected with `not_supported`.", "operationId": "updateProductLesson", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "lessonId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LessonUpdateRequest" }, "example": { "title": "Updated Introduction to AI", "published": true } } } }, "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Lesson" } } } }, "422": { "description": "SCORM lessons are not supported, or lesson validation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } }, "delete": { "tags": ["Product Content"], "summary": "Delete a product lesson", "operationId": "deleteProductLesson", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "lessonId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkResponse" } } } }, "422": { "description": "Lesson could not be deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/lessons/{lessonId}/move": { "post": { "tags": ["Product Content"], "summary": "Move a lesson to another section", "operationId": "moveProductLesson", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "lessonId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "destinationSectionId": { "type": "string" }, "destinationIndex": { "type": "integer" } }, "required": [ "destinationSectionId", "destinationIndex" ] } } } }, "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkResponse" } } } }, "422": { "description": "Lesson could not be moved.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/customers": { "get": { "tags": ["Product Customers"], "summary": "List product customers", "operationId": "listProductCustomers", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "search", "in": "query", "description": "Search customers by name or email.", "schema": { "type": "string" } }, { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1, "minimum": 1 } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "minimum": 1, "maximum": 200 } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerListResponse" } } } } } } }, "/api/products/{productId}/customers/invitations": { "post": { "tags": ["Product Customers"], "summary": "Invite a customer", "description": "Invites a customer by email. An invitation email is sent to the provided address.", "operationId": "inviteProductCustomer", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "email": { "type": "string", "format": "email" }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": ["email"] } } } }, "responses": { "201": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Customer" } } } }, "400": { "description": "Unsupported customer invitation field or missing email.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "422": { "description": "Customer could not be invited.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/customers/{userId}/progress": { "get": { "tags": ["Product Customers"], "summary": "Get product customer progress", "description": "Returns customer progress details including completed lessons.", "operationId": "getProductCustomerProgress", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Progress" } } } }, "404": { "description": "Customer progress not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/customers/{userId}/lessons/{lessonId}/evaluations": { "post": { "tags": ["Product Learner Actions"], "summary": "Evaluate a quiz lesson for a product customer", "description": "Evaluates quiz answers for the target product customer using existing CourseLit learner runtime behavior. This records the evaluation result but does not mark the lesson complete.", "operationId": "evaluateProductCustomerLesson", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "lessonId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LessonEvaluationRequest" }, "example": { "answers": [[0], [1, 2]] } } } }, "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LessonEvaluationResult" } } } }, "400": { "description": "Answers are missing or malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "404": { "description": "Customer or lesson not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "422": { "description": "Lesson could not be evaluated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/products/{productId}/customers/{userId}/lessons/{lessonId}/completion": { "post": { "tags": ["Product Learner Actions"], "summary": "Mark a product customer lesson complete", "description": "Marks a lesson complete for the target product customer using existing CourseLit learner runtime behavior. Quiz lessons still require a passing evaluation first.", "operationId": "completeProductCustomerLesson", "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "lessonId", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LessonCompletionResponse" } } } }, "404": { "description": "Customer or lesson not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } }, "422": { "description": "Lesson could not be marked complete.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicApiErrorResponse" } } } } } } }, "/api/media/presigned": { "post": { "tags": ["Media Uploads"], "summary": "Generate a MediaLit upload signature", "description": "Returns a short-lived upload signature and endpoint for direct file uploads. See `https://docs.medialit.cloud/api/uploadMedia` for the upload request format.", "operationId": "createMediaUploadSignature", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "description": "MediaLit upload signature generated successfully.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MediaPresignedResponse" }, "examples": { "success": { "value": { "signature": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "endpoint": "https://media.example.com" } } } } } }, "401": { "description": "Invalid API key, or no active CourseLit dashboard session was found for the dashboard-only auth path." }, "403": { "description": "The resolved school owner or logged-in dashboard user does not have `media:manage` permission.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MediaErrorResponse" } } } }, "404": { "description": "Domain not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MediaErrorResponse" } } } }, "500": { "description": "MediaLit signature generation failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MediaErrorResponse" } } } } } } } }, "components": { "schemas": { "CreateUserRequest": { "type": "object", "required": ["email"], "properties": { "apikey": { "type": "string", "description": "Deprecated legacy API key transport. Prefer the `x-api-key` header.", "deprecated": true }, "email": { "type": "string", "format": "email", "description": "Email address of the user to create." }, "name": { "type": "string", "description": "Display name for the user." }, "permissions": { "type": "array", "description": "Permissions to assign to the user.", "items": { "type": "string", "enum": [ "course:manage", "course:manage_any", "course:publish", "course:enroll", "media:manage", "site:manage", "setting:manage", "user:manage", "community:manage" ] } }, "subscribedToUpdates": { "type": "boolean", "description": "Whether the user should be subscribed to marketing updates." } } }, "UpdateUserRequest": { "type": "object", "required": ["email"], "properties": { "apikey": { "type": "string", "description": "Deprecated legacy API key transport. Prefer the `x-api-key` header.", "deprecated": true }, "email": { "type": "string", "format": "email", "description": "Email address of the user to update." }, "name": { "type": "string", "description": "Updated display name for the user." }, "permissions": { "type": "array", "description": "Updated permissions for the user.", "items": { "type": "string", "enum": [ "course:manage", "course:manage_any", "course:publish", "course:enroll", "media:manage", "site:manage", "setting:manage", "user:manage", "community:manage" ] } }, "subscribedToUpdates": { "type": "boolean", "description": "Updated marketing subscription preference for the user." } } }, "UserMutationSuccess": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "description": "MD5 hash of the user email.", "example": "5d41402abc4b2a76b9719d911017c592" } } }, "ErrorResponse": { "type": "object", "description": "Error payload returned by the CourseLit user API. Depending on the code path, the message may appear in `message` or `error`.", "properties": { "message": { "type": "string", "description": "Error message returned by validation and auth failures.", "example": "Bad request" }, "error": { "type": "string", "description": "Error message returned by application-level failures.", "example": "Internal server error" } } }, "PublicApiErrorResponse": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" } }, "required": ["code", "message"] } }, "required": ["error"] }, "OkResponse": { "type": "object", "properties": { "ok": { "type": "boolean" } } }, "PaymentPlan": { "type": "object", "properties": { "planId": { "type": "string" }, "name": { "type": "string" }, "type": { "type": "string", "enum": ["free", "onetime", "emi", "subscription"] }, "entityId": { "type": "string" }, "entityType": { "type": "string" }, "oneTimeAmount": { "type": "number" }, "emiAmount": { "type": "number" }, "emiTotalInstallments": { "type": "number" }, "subscriptionMonthlyAmount": { "type": "number" }, "subscriptionYearlyAmount": { "type": "number" }, "description": { "type": "string" }, "isDefault": { "type": "boolean" } } }, "PaymentPlanCreateRequest": { "type": "object", "description": "Create a product-owned payment plan. `onetime` requires `oneTimeAmount`; `emi` requires `emiAmount` and `emiTotalInstallments`; `subscription` requires exactly one of `subscriptionMonthlyAmount` or `subscriptionYearlyAmount`.", "required": ["name", "type"], "properties": { "name": { "type": "string", "description": "Payment plan name shown to customers." }, "type": { "type": "string", "enum": ["free", "onetime", "emi", "subscription"] }, "oneTimeAmount": { "type": "number", "description": "Required when `type` is `onetime`." }, "emiAmount": { "type": "number", "description": "Required when `type` is `emi`." }, "emiTotalInstallments": { "type": "number", "description": "Required when `type` is `emi`." }, "subscriptionMonthlyAmount": { "type": "number", "description": "Use for monthly subscriptions. For `subscription`, provide exactly one subscription amount." }, "subscriptionYearlyAmount": { "type": "number", "description": "Use for yearly subscriptions. For `subscription`, provide exactly one subscription amount." }, "description": { "type": "string" } } }, "PaymentPlanUpdateRequest": { "type": "object", "description": "Update editable payment plan fields.", "properties": { "name": { "type": "string", "description": "Payment plan name shown to customers." }, "type": { "type": "string", "enum": ["free", "onetime", "emi", "subscription"] }, "oneTimeAmount": { "type": "number", "description": "Required when `type` is `onetime`." }, "emiAmount": { "type": "number", "description": "Required when `type` is `emi`." }, "emiTotalInstallments": { "type": "number", "description": "Required when `type` is `emi`." }, "subscriptionMonthlyAmount": { "type": "number", "description": "Use for monthly subscriptions. For `subscription`, provide exactly one subscription amount." }, "subscriptionYearlyAmount": { "type": "number", "description": "Use for yearly subscriptions. For `subscription`, provide exactly one subscription amount." }, "description": { "type": "string" } } }, "PaymentPlanListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentPlan" } } } }, "Product": { "type": "object", "properties": { "productId": { "type": "string" }, "type": { "type": "string", "enum": ["course", "download", "blog"] }, "title": { "type": "string" }, "slug": { "type": "string" }, "description": { "type": "string" }, "published": { "type": "boolean" }, "privacy": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } }, "featuredImage": { "type": "object" }, "pageId": { "type": "string" }, "defaultPaymentPlan": { "type": "string" }, "paymentPlans": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentPlan" } }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } } }, "ProductCreateRequest": { "type": "object", "required": ["title", "type"], "description": "Payload for creating a draft product. Send `title` and `type`. After creation, use the update endpoint for metadata, publish, and privacy changes.", "properties": { "title": { "type": "string", "description": "Product title shown in CourseLit.", "example": "AI Foundations" }, "type": { "type": "string", "enum": ["course", "download", "blog"], "description": "CourseLit product type. Use `course` for lesson-based learning products.", "example": "course" } } }, "ProductUpdateRequest": { "type": "object", "description": "Payload for updating product metadata. A product must have at least one payment plan before it can be published.", "properties": { "title": { "type": "string", "description": "Product title shown in CourseLit.", "example": "AI Foundations" }, "slug": { "type": "string", "description": "Optional URL slug.", "example": "ai-foundations" }, "description": { "type": "string", "description": "Optional product/blog description. Send this as a JSON-stringified Tiptap/ProseMirror document, for example `JSON.stringify({ type: \"doc\", content: [] })`. Do not use a `content` field on this endpoint.", "example": "{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Updated course description.\"}]}]}" }, "published": { "type": "boolean", "description": "Whether the product is published. Existing CourseLit publishing checks still apply.", "example": false }, "privacy": { "type": "string", "description": "Existing CourseLit product privacy value.", "example": "unlisted" }, "tags": { "type": "array", "items": { "type": "string" } }, "featuredImage": { "type": "object" } } }, "ProductListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" } } } } }, "Section": { "type": "object", "properties": { "sectionId": { "type": "string" }, "name": { "type": "string" }, "rank": { "type": "number" }, "collapsed": { "type": "boolean" }, "drip": { "$ref": "#/components/schemas/SectionDrip" }, "lessonsOrder": { "type": "array", "items": { "type": "string" } } } }, "SectionCreateRequest": { "type": "object", "required": ["name"], "description": "Payload for creating a section. Requires only `name`.", "properties": { "name": { "type": "string" } } }, "SectionUpdateRequest": { "type": "object", "description": "Payload for updating a section. Supports updating `name` and scheduled release (`drip`) settings.", "properties": { "name": { "type": "string" }, "drip": { "$ref": "#/components/schemas/SectionDripInput" } } }, "SectionDripInput": { "type": "object", "properties": { "type": { "type": "string", "enum": ["relative-date", "exact-date"] }, "status": { "type": "boolean" }, "delayInMillis": { "type": "number", "description": "Delay in milliseconds for `relative-date` drip. The input accepts a number interpreted as days (e.g. 3 = three days), but the value is persisted in millisecond equivalent (e.g. 259200000). The endpoint output always returns the stored millisecond value." }, "dateInUTC": { "type": "number", "description": "UNIX timestamp in milliseconds for `exact-date` drip. Both input and output use millisecond precision." } } }, "SectionDrip": { "$ref": "#/components/schemas/SectionDripInput" }, "SectionListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Section" } } } }, "TiptapDocument": { "type": "object", "description": "Tiptap/ProseMirror document JSON used by `text` lessons. Supported nodes include `doc`, `paragraph`, `heading` (`level` 1, 2, or 3), `text`, `bulletList`, `orderedList`, `listItem`, `blockquote`, `horizontalRule`, `codeBlock`, `table`, `tableRow`, `tableHeader`, `tableCell`, `image`, and `hardBreak`. Supported marks include `bold`, `italic`, `underline`, `strike`, `code`, `link`, and `highlight`. An `image` node uses `attrs` with `src` (required), `alt` (optional), and `title` (optional).", "required": ["type", "content"], "properties": { "type": { "type": "string", "example": "doc" }, "content": { "type": "array", "items": { "type": "object" } } } }, "EmbedContent": { "type": "object", "description": "Embed content for `embed` lessons. The value can be a supported video URL or iframe/embed code, matching the dashboard Embed lesson field.", "required": ["value"], "properties": { "value": { "type": "string", "example": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" } } }, "QuizContent": { "type": "object", "description": "Quiz content for `quiz` lesson type.", "required": [ "questions", "requiresPassingGrade", "passingGrade" ], "properties": { "questions": { "type": "array", "description": "List of quiz questions.", "items": { "type": "object", "required": ["text", "options"], "properties": { "text": { "type": "string", "description": "Question text." }, "options": { "type": "array", "description": "Answer options. Exactly one option should have `correctAnswer: true` for single-choice; multiple for multiple-choice.", "items": { "type": "object", "required": ["text"], "properties": { "text": { "type": "string", "description": "Option text." }, "correctAnswer": { "type": "boolean", "description": "Whether this is the correct answer. Stripped from student-facing responses." } } } } } } }, "requiresPassingGrade": { "type": "boolean", "description": "Whether a minimum score is required to pass." }, "passingGrade": { "type": "number", "description": "Score threshold (0–100) when `requiresPassingGrade` is true." } } }, "LessonMedia": { "type": "object", "description": "Media object used by `video`, `audio`, `pdf`, and `file` lessons. Use `/api/media/presigned` to upload to MediaLit first, then send the resulting media metadata here.", "required": ["mediaId"], "properties": { "mediaId": { "type": "string" }, "originalFileName": { "type": "string" }, "mimeType": { "type": "string" }, "size": { "type": "number" }, "access": { "type": "string", "enum": ["public", "private"] }, "file": { "type": "string", "description": "Public file URL when available. Private media may omit this field." }, "thumbnail": { "type": "string" }, "caption": { "type": "string" } } }, "Lesson": { "type": "object", "properties": { "lessonId": { "type": "string" }, "title": { "type": "string" }, "type": { "type": "string", "enum": [ "text", "video", "audio", "pdf", "file", "embed", "quiz" ] }, "content": { "oneOf": [ { "$ref": "#/components/schemas/TiptapDocument" }, { "$ref": "#/components/schemas/EmbedContent" }, { "$ref": "#/components/schemas/QuizContent" } ], "description": "`text` lessons use `TiptapDocument`; `embed` lessons use `EmbedContent`; `quiz` lessons use `QuizContent`. Media-backed lessons (`video`, `audio`, `pdf`, `file`) use `media` instead of `content`." }, "media": { "$ref": "#/components/schemas/LessonMedia" }, "downloadable": { "type": "boolean" }, "courseId": { "type": "string" }, "groupId": { "type": "string" }, "requiresEnrollment": { "type": "boolean" }, "published": { "type": "boolean" } } }, "LessonGroup": { "type": "object", "properties": { "groupId": { "type": "string" }, "lessons": { "type": "array", "items": { "$ref": "#/components/schemas/Lesson" } } } }, "LessonCreateRequest": { "type": "object", "required": ["title", "type", "groupId"], "properties": { "title": { "type": "string" }, "type": { "type": "string", "enum": [ "text", "video", "audio", "pdf", "file", "embed", "quiz" ] }, "content": { "oneOf": [ { "$ref": "#/components/schemas/TiptapDocument" }, { "$ref": "#/components/schemas/EmbedContent" }, { "$ref": "#/components/schemas/QuizContent" } ], "description": "`text` lessons use `TiptapDocument`; `embed` lessons use `EmbedContent`; `quiz` lessons use `QuizContent`. Media-backed lessons (`video`, `audio`, `pdf`, `file`) use `media` instead of `content`." }, "media": { "$ref": "#/components/schemas/LessonMedia" }, "downloadable": { "type": "boolean" }, "groupId": { "type": "string" }, "requiresEnrollment": { "type": "boolean" }, "published": { "type": "boolean" } } }, "LessonUpdateRequest": { "type": "object", "properties": { "title": { "type": "string" }, "content": { "oneOf": [ { "$ref": "#/components/schemas/TiptapDocument" }, { "$ref": "#/components/schemas/EmbedContent" }, { "$ref": "#/components/schemas/QuizContent" } ], "description": "`text` lessons use `TiptapDocument`; `embed` lessons use `EmbedContent`; `quiz` lessons use `QuizContent`. Media-backed lessons (`video`, `audio`, `pdf`, `file`) use `media` instead of `content`." }, "media": { "$ref": "#/components/schemas/LessonMedia" }, "downloadable": { "type": "boolean" }, "requiresEnrollment": { "type": "boolean" }, "published": { "type": "boolean" } } }, "LessonListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/LessonGroup" } } } }, "Customer": { "type": "object", "properties": { "userId": { "type": "string" }, "email": { "type": "string" }, "name": { "type": "string" }, "avatar": { "type": "object" }, "membershipId": { "type": "string" }, "membershipStatus": { "type": "string" }, "subscriptionMethod": { "type": "string" }, "completedLessons": { "type": "array", "items": { "type": "string" } }, "downloaded": { "type": "boolean" }, "enrolledAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } } }, "ProductCustomer": { "type": "object", "description": "Member enrollment details returned by the product customers endpoint.", "properties": { "user": { "type": "object", "properties": { "userId": { "type": "string" }, "email": { "type": "string" }, "name": { "type": "string" }, "avatar": { "type": "object" } } }, "status": { "type": "string" }, "completedLessons": { "type": "array", "items": { "type": "string" } }, "downloaded": { "type": "boolean" }, "subscriptionMethod": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } } }, "CustomerListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ProductCustomer" } }, "pagination": { "type": "object", "properties": { "page": { "type": "integer" }, "limit": { "type": "integer" } } } } }, "Progress": { "type": "object", "properties": { "courseId": { "type": "string" }, "completedLessons": { "type": "array", "items": { "type": "string" } }, "downloaded": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } } }, "LessonEvaluationRequest": { "type": "object", "required": ["answers"], "properties": { "answers": { "type": "array", "description": "Quiz answers by question index. Each nested array contains selected option indexes for that question.", "items": { "type": "array", "items": { "type": "number" } } } } }, "LessonEvaluationResult": { "type": "object", "properties": { "pass": { "type": "boolean" }, "score": { "type": "number" }, "requiresPassingGrade": { "type": "boolean" }, "passingGrade": { "type": "number" } } }, "LessonCompletionResponse": { "type": "object", "properties": { "completed": { "type": "boolean" } } }, "MediaPresignedResponse": { "type": "object", "required": ["signature", "endpoint"], "properties": { "signature": { "type": "string", "description": "MediaLit upload signature. Send this as the `x-medialit-signature` header to MediaLit." }, "endpoint": { "type": "string", "format": "uri", "description": "MediaLit server endpoint. Upload files directly to `${endpoint}/media/create` for multipart uploads or `${endpoint}/media/create/resumable` for TUS resumable uploads." } } }, "MediaErrorResponse": { "type": "object", "properties": { "message": { "type": "string", "example": "Domain not found" }, "error": { "type": "string", "example": "Unable to generate media signature" } } } }, "parameters": {}, "securitySchemes": { "ApiKeyAuth": { "type": "apiKey", "in": "header", "name": "x-api-key", "description": "API key created in CourseLit dashboard settings. The legacy `apikey` request-body field is still accepted but deprecated." } } } }