{ "openapi": "3.1.0", "info": { "title": "Main Frontend API", "description": "", "contact": { "name": "University of Helsinki", "email": "mooc@cs.helsinki.fi" }, "license": { "name": "Apache-2.0", "identifier": "Apache-2.0" }, "version": "0.1.0" }, "paths": { "/api/v0/files/{exercise_service_slug}": { "post": { "tags": ["files"], "summary": "\nPOST `/api/v0/files/:exercise_service_slug`\nUsed to upload data from exercise service iframes.\n\n# Returns\nAn ordered list of host-assigned file ids and stored URLs.", "operationId": "uploadFilesFromExerciseService", "parameters": [ { "name": "exercise_service_slug", "in": "path", "description": "Exercise service slug", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "multipart/form-data": { "schema": { "type": "object", "additionalProperties": { "type": "string", "format": "binary" }, "propertyNames": { "type": "string" } } } }, "required": true }, "responses": { "200": { "description": "Uploaded files", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseServiceUploadResultEntry" } } } } } } } }, "/api/v0/main-frontend/certificates": { "post": { "tags": ["certificates"], "summary": "\nPOST `/api/v0/main-frontend/certificates/`\n\nUpdates the certificate configuration for a given module.", "operationId": "updateCertificateConfiguration", "requestBody": { "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["metadata", "file"], "properties": { "file": { "type": "array", "items": { "type": "string", "format": "binary" }, "contentMediaType": "application/octet-stream" }, "metadata": { "$ref": "#/components/schemas/CertificateConfigurationUpdate" } } }, "encoding": { "metadata": { "contentType": "application/json" } } } }, "required": true }, "responses": { "200": { "description": "Certificate configuration updated", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/certificates/configuration/{certificate_configuration_id}": { "delete": { "tags": ["certificates"], "summary": "\nDELETE `/api/v0/main-frontend/certificates/configuration/{configuration_id}`\n\nDeletes the given configuration.", "operationId": "deleteCertificateConfiguration", "parameters": [ { "name": "certificate_configuration_id", "in": "path", "description": "Certificate configuration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Certificate configuration deleted", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/certificates/generate": { "post": { "tags": ["certificates"], "summary": "\nPOST `/api/v0/main-frontend/certificates/generate`\n\nGenerates a certificate for a given certificate configuration id.", "operationId": "generateCertificate", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CertificateGenerationRequest" } } }, "required": true }, "responses": { "200": { "description": "Certificate generated", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/certificates/generated/{certificate_id}": { "put": { "tags": ["certificates"], "operationId": "updateGeneratedCertificate", "parameters": [ { "name": "certificate_id", "in": "path", "description": "Generated certificate id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CertificateUpdateRequest" } } }, "required": true }, "responses": { "200": { "description": "Generated certificate updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GeneratedCertificate" } } } } } } }, "/api/v0/main-frontend/certificates/get-by-configuration-id/{certificate_configuration_id}": { "get": { "tags": ["certificates"], "summary": "\nGET `/api/v0/main-frontend/certificates/get-by-configuration-id/{certificate_configuration_id}`\n\nFetches the user's certificate for the given course module and course instance.", "operationId": "getCertificateByConfigurationId", "parameters": [ { "name": "certificate_configuration_id", "in": "path", "description": "Certificate configuration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Generated certificate", "content": { "application/json": { "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/GeneratedCertificate" } ] } } } } } } }, "/api/v0/main-frontend/certificates/{certificate_verification_id}": { "get": { "tags": ["certificates"], "summary": "\nGET `/api/v0/main-frontend/certificates/{certificate_verification_id}`\n\nFetches the user's certificate using the verification id.\n\nResponse: the certificate as a png.", "operationId": "getCertificateByVerificationId", "parameters": [ { "name": "certificate_verification_id", "in": "path", "description": "Certificate verification id", "required": true, "schema": { "type": "string" } }, { "name": "debug", "in": "query", "description": "Whether to render a debug certificate", "required": true, "schema": { "type": "boolean" } }, { "name": "test_certificate_configuration_id", "in": "query", "description": "Certificate configuration id to use for preview rendering", "required": false, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Certificate image", "content": { "image/png": { "schema": {} } } } } } }, "/api/v0/main-frontend/chapters": { "post": { "tags": ["chapters"], "summary": "\nPOST `/api/v0/main-frontend/chapters` - Create a new course part.\n# Example\n\nRequest:\n```http\nPOST /api/v0/main-frontend/chapters HTTP/1.1\nContent-Type: application/json\n\n{\n \"name\": \"The Basics\",\n \"course_id\": \"d86cf910-4d26-40e9-8c9c-1cc35294fdbb\",\n \"chapter_number\": 1,\n \"front_page_id\": null\n}\n```", "operationId": "createChapter", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewChapter" } } }, "required": true }, "responses": { "200": { "description": "Created chapter", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Chapter" } } } } } } }, "/api/v0/main-frontend/chapters/{chapter_id}": { "put": { "tags": ["chapters"], "summary": "\nPUT `/api/v0/main-frontend/chapters/:chapter_id` - Update chapter.\n# Example\n\nRequest:\n```http\nPUT /api/v0/main-frontend/chapters/d332f3d9-39a5-4a18-80f4-251727693c37 HTTP/1.1\nContent-Type: application/json\n\n{\n \"name\": \"The Basics\",\n \"chapter_image_url\": null,\n \"chapter_number\": 2,\n \"front_page_id\": \"0ebba931-b027-4154-8274-2afb00d79306\"\n}\n\n```", "operationId": "updateChapter", "parameters": [ { "name": "chapter_id", "in": "path", "description": "Chapter id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChapterUpdate" } } }, "required": true }, "responses": { "200": { "description": "Updated chapter", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Chapter" } } } } } }, "delete": { "tags": ["chapters"], "summary": "\nDELETE `/api/v0/main-frontend/chapters/:chapter_id` - Delete a chapter.", "operationId": "deleteChapter", "parameters": [ { "name": "chapter_id", "in": "path", "description": "Chapter id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted chapter", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Chapter" } } } } } } }, "/api/v0/main-frontend/chapters/{chapter_id}/image": { "put": { "tags": ["chapters"], "summary": "\nPUT `/api/v0/main-frontend/chapters/:chapter_id/image` - Sets or updates the chapter image.\n\n# Example\n\nRequest:\n```http\nPUT /api/v0/main-frontend/chapters/d332f3d9-39a5-4a18-80f4-251727693c37/image HTTP/1.1\nContent-Type: multipart/form-data\n\nBINARY_DATA\n```", "operationId": "updateChapterImage", "parameters": [ { "name": "chapter_id", "in": "path", "description": "Chapter id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "array", "items": { "type": "integer", "format": "int32", "minimum": 0 }, "contentMediaType": "application/octet-stream" } } } } }, "required": true }, "responses": { "200": { "description": "Updated chapter image", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Chapter" } } } } } }, "delete": { "tags": ["chapters"], "summary": "\nDELETE `/api/v0/main-frontend/chapters/:chapter_id/image` - Removes the chapter image.\n\n# Example\n\nRequest:\n```http\nDELETE /api/v0/main-frontend/chapters/d332f3d9-39a5-4a18-80f4-251727693c37/image HTTP/1.1\n```", "operationId": "deleteChapterImage", "parameters": [ { "name": "chapter_id", "in": "path", "description": "Chapter id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted chapter image" } } } }, "/api/v0/main-frontend/chapters/{course_id}/all-chapters-for-course": { "get": { "tags": ["chapters"], "summary": "\nGET `/api/v0/main-frontend/chapters/{course_id}/all-chapters-for-course - Gets all chapters with a course_id", "operationId": "getCourseChapters", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course chapters", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/DatabaseChapter" } } } } } } } }, "/api/v0/main-frontend/chatbot-models/": { "get": { "tags": ["chatbot-models"], "summary": "GET `/api/v0/main-frontend/chatbot-models?course_id={course_id}`", "operationId": "getChatbotModels", "parameters": [ { "name": "course_id", "in": "query", "description": "Course id", "required": false, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Chatbot models", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ChatbotConfigurationModel" } } } } } } } }, "/api/v0/main-frontend/chatbot-models/{chatbot_model_id}": { "get": { "tags": ["chatbot-models"], "summary": "GET `/api/v0/main-frontend/chatbot-models/{chatbot_configuration_id}`", "operationId": "getChatbotModel", "parameters": [ { "name": "chatbot_model_id", "in": "path", "description": "Chatbot model id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "string", "format": "uuid" } } }, "required": true }, "responses": { "200": { "description": "Chatbot model", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatbotConfigurationModel" } } } } } } }, "/api/v0/main-frontend/chatbots/": { "get": { "tags": ["chatbots"], "summary": "GET `/api/v0/main-frontend/chatbots`", "operationId": "getAllChatbots", "responses": { "200": { "description": "All chatbots", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ChatbotConfiguration" } } } } } } } }, "/api/v0/main-frontend/chatbots/create": { "post": { "tags": ["chatbots"], "summary": "POST `/api/v0/main-frontend/chatbots/create`", "operationId": "createChatbot", "requestBody": { "description": "JSON object with chatbot name and optional course id, e.g. \"name: Chatbot 1, course_id: null\".", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateChatbotRequest" } } }, "required": true }, "responses": { "200": { "description": "Created chatbot", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatbotConfiguration" } } } } } } }, "/api/v0/main-frontend/chatbots/{chatbot_configuration_id}": { "get": { "tags": ["chatbots"], "summary": "GET `/api/v0/main-frontend/chatbots/{chatbot_configuration_id}`", "operationId": "getChatbotConfiguration", "parameters": [ { "name": "chatbot_configuration_id", "in": "path", "description": "Chatbot configuration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Chatbot configuration", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatbotConfiguration" } } } } } }, "post": { "tags": ["chatbots"], "summary": "POST `/api/v0/main-frontend/chatbots/{chatbot_configuration_id}`", "operationId": "configureChatbot", "parameters": [ { "name": "chatbot_configuration_id", "in": "path", "description": "Chatbot configuration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewChatbotConf" } } }, "required": true }, "responses": { "200": { "description": "Updated chatbot configuration", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatbotConfiguration" } } } }, "403": { "description": "Enabling or disabling admin-support tool categories requires global admin permissions" } } }, "delete": { "tags": ["chatbots"], "summary": "DELETE `/api/v0/main-frontend/chatbots/{chatbot_configuration_id}`", "operationId": "deleteChatbotConfiguration", "parameters": [ { "name": "chatbot_configuration_id", "in": "path", "description": "Chatbot configuration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted chatbot configuration" } } } }, "/api/v0/main-frontend/code-giveaways": { "post": { "tags": ["code_giveaways"], "summary": "\n * POST `/api/v0/main-frontend/code-giveaways - Creates a new code giveaway.", "operationId": "createCodeGiveaway", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewCodeGiveaway" } } }, "required": true }, "responses": { "200": { "description": "Created code giveaway", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CodeGiveaway" } } } } } } }, "/api/v0/main-frontend/code-giveaways/by-course/{course_id}": { "get": { "tags": ["code_giveaways"], "summary": "\nGET `/api/v0/main-frontend/code-giveaways/by-course/:course_id` - Returns code giveaways for a course.", "operationId": "getCodeGiveawaysByCourse", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Code giveaways for course", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CodeGiveaway" } } } } } } } }, "/api/v0/main-frontend/code-giveaways/{id}": { "get": { "tags": ["code_giveaways"], "summary": "\n * GET `/api/v0/main-frontend/code-giveaways/:id - Gets a code giveaway by ID.", "operationId": "getCodeGiveawayById", "parameters": [ { "name": "id", "in": "path", "description": "Code giveaway id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Code giveaway", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CodeGiveaway" } } } } } } }, "/api/v0/main-frontend/code-giveaways/{id}/codes": { "get": { "tags": ["code_giveaways"], "summary": "\n * GET `/api/v0/main-frontend/code-giveaways/:id/codes - Gets codes for a code giveaway by ID.", "operationId": "getCodeGiveawayCodes", "parameters": [ { "name": "id", "in": "path", "description": "Code giveaway id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Giveaway codes", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CodeGiveawayCode" } } } } } } }, "post": { "tags": ["code_giveaways"], "summary": "\n * POST `/api/v0/main-frontend/code-giveaways/:id/codes - Adds new codes to a code giveaway.", "operationId": "addCodeGiveawayCodes", "parameters": [ { "name": "id", "in": "path", "description": "Code giveaway id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } }, "required": true }, "responses": { "200": { "description": "Added giveaway codes", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CodeGiveawayCode" } } } } } } } }, "/api/v0/main-frontend/code-giveaways/{id}/codes/csv": { "get": { "tags": ["code_giveaways"], "summary": "\n * GET `/api/v0/main-frontend/code-giveaways/:id/codes/csv - Gets codes for a code giveaway by ID as CSV.", "operationId": "downloadCodeGiveawayCodesCsv", "parameters": [ { "name": "id", "in": "path", "description": "Code giveaway id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Giveaway codes CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/code-giveaways/{id}/codes/{code_id}": { "delete": { "tags": ["code_giveaways"], "summary": "\n * DELETE `/api/v0/main-frontend/code-giveaways/:id/codes/:code_id - Deletes a code giveaway code.", "operationId": "deleteCodeGiveawayCode", "parameters": [ { "name": "id", "in": "path", "description": "Code giveaway id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "code_id", "in": "path", "description": "Giveaway code id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted giveaway code" } } } }, "/api/v0/main-frontend/course-auditing/": { "get": { "tags": ["course_auditing"], "summary": "\nGET `/api/v0/main-frontend/course-auditing`", "operationId": "getCoursesForAuditing", "responses": { "200": { "description": "Courses for auditing", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseAuditingData" } } } } } } } }, "/api/v0/main-frontend/course-auditing/{course_id}": { "put": { "tags": ["course_auditing"], "summary": "\nPUT `/api/v0/main-frontend/course-auditing/:id`", "operationId": "updateCourseAuditingData", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseAuditingDataUpdate" } } }, "required": true }, "responses": { "200": { "description": "Updated course", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseAuditingData" } } } } } } }, "/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/actions": { "get": { "tags": ["course-credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/actions` - The manual\nactions taken on this course's credit registrations, newest first.", "operationId": "getCourseCreditRegistrationActions", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The course's manual actions", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseCreditRegistrationAction" } } } } } } } }, "/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/by-user-ids": { "post": { "tags": ["course-credit-registrations"], "summary": "\nPOST `/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/by-user-ids` - The\nregistrations of the named students, for the students tab's current page.", "operationId": "getCourseCreditRegistrationsForUsers", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseCreditRegistrationUserIdsPayload" } } }, "required": true }, "responses": { "200": { "description": "The named students' registrations", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseCreditRegistration" } } } } } } } }, "/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/export": { "get": { "tags": ["course-credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/export` - Every credit\nregistration of the course as csv.", "operationId": "exportCourseCreditRegistrations", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The course's credit registrations", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/list": { "get": { "tags": ["course-credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/list` - A page of the\ncourse's registrations, filtered by state and searched by student name, email or student number.", "operationId": "getCourseCreditRegistrations", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number, from 1", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "limit", "in": "query", "description": "Rows per page", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "search", "in": "query", "description": "Student name, email or student number", "required": false, "schema": { "type": "string" } }, { "name": "state", "in": "query", "description": "Ledger state filter", "required": false, "schema": { "$ref": "#/components/schemas/CreditRegistrationState" } }, { "name": "course_instance_id", "in": "query", "description": "Course instance filter", "required": false, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "A page of the course's registrations", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseCreditRegistrationsPage" } } } } } } }, "/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/module-configs": { "get": { "tags": ["course-credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/module-configs` - The\ncourse's per-module credit registration configuration.", "operationId": "getCourseCreditRegistrationModuleConfigs", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The course's per-module configuration", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseCreditRegistrationModuleConfigs" } } } } } } }, "/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/resend-linking-email": { "post": { "tags": ["course-credit-registrations"], "summary": "\nPOST\n`/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/resend-linking-email` - Sets off\nanother account-linking mail for one person on this course.\n\nThe target has to be on this course's roster in the study registry and hold no link with us. The caps\nof the ordinary claim path apply and nothing here relaxes them.", "operationId": "resendCourseCreditRegistrationLinkingEmail", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResendLinkingEmailPayload" } } }, "required": true }, "responses": { "200": { "description": "What the attempt did", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResendLinkingEmailResult" } } } }, "400": { "description": "Nothing named, or this teacher has set off too many mails this hour" } } } }, "/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/retry-failed": { "post": { "tags": ["course-credit-registrations"], "summary": "\nPOST `/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/retry-failed` - Puts this\ncourse's failed registrations back on the pipeline.\n\nRefuses the same rows the single-row retry refuses and reports how many of each it left alone rather\nthan failing the whole call over them. The cap applies to the rows a retry can actually move: the\nrefused ones are counted across the whole course, because they would otherwise sit in the batch\nforever and a course that accumulated a capful of them could never retry anything again.", "operationId": "retryFailedCreditRegistrationsForCourse", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RetryCreditRegistrationPayload" } } }, "required": true }, "responses": { "200": { "description": "How many were retried and why the rest were not", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RetryFailedCreditRegistrationsResult" } } } } } } }, "/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/summary": { "get": { "tags": ["course-credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/course-credit-registrations/courses/{course_id}/summary` - Per-module\ncounts plus the two reasons a student of this course will not get credits.", "operationId": "getCourseCreditRegistrationSummary", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The course's credit registration summary", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseCreditRegistrationSummary" } } } } } } }, "/api/v0/main-frontend/course-credit-registrations/registrations/{credit_registration_id}": { "get": { "tags": ["course-credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/course-credit-registrations/registrations/{credit_registration_id}` - One\nregistration with its timeline and the other attempts for the same completion.\n\nAuthorized on the row's own course: a course id from the caller would let a teacher of one course pair\nit with a foreign registration id.", "operationId": "getCreditRegistrationDetails", "parameters": [ { "name": "credit_registration_id", "in": "path", "description": "Credit registration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The registration with its timeline", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationDetails" } } } }, "404": { "description": "No such registration" } } } }, "/api/v0/main-frontend/course-credit-registrations/registrations/{credit_registration_id}/retry": { "post": { "tags": ["course-credit-registrations"], "summary": "\nPOST `/api/v0/main-frontend/course-credit-registrations/registrations/{credit_registration_id}/retry`\n- Puts one failed registration back on the pipeline.\n\nAuthorized on the row's own course, which is why no course id appears in the path: a teacher of one\ncourse must not be able to pair it with a foreign registration id.", "operationId": "retryCreditRegistration", "parameters": [ { "name": "credit_registration_id", "in": "path", "description": "Credit registration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RetryCreditRegistrationPayload" } } }, "required": true }, "responses": { "200": { "description": "What the retry did", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RetryCreditRegistrationResult" } } } }, "404": { "description": "No such registration" } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}": { "get": { "tags": ["course-instances"], "summary": "\nGET /course-instances/:id", "operationId": "getCourseInstance", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course instance", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseInstance" } } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/completions": { "get": { "tags": ["course-instances"], "summary": "\nGET `/api/v0/main-frontend/course-instances/{course_instance_id}/completions`", "operationId": "getCourseInstanceCompletions", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course instance completion summary", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseInstanceCompletionSummary" } } } } } }, "post": { "tags": ["course-instances"], "summary": "\nPOST `/api/v0/main-frontend/course-instances/{course_instance_id}/completions`", "operationId": "createCourseInstanceCompletions", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TeacherManualCompletionRequest" } } }, "required": true }, "responses": { "200": { "description": "Manual completions added" } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/completions/preview": { "post": { "tags": ["course-instances"], "operationId": "previewCourseInstanceCompletions", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TeacherManualCompletionRequest" } } }, "required": true }, "responses": { "200": { "description": "Manual completion preview", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ManualCompletionPreview" } } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/course-module-completions/{user_id}": { "get": { "tags": ["course-instances"], "summary": "\nGET /course-instances/:id/course-module-completions/:user_id - Returns a list of all course module completions for a given user for this course instance.", "operationId": "getCourseInstanceCourseModuleCompletionsForUser", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course module completions for user", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/default-certificate-configurations": { "get": { "tags": ["course-instances"], "summary": "\nGET /course-instances/:id/default-certificate-configurations - gets default certificate configurations of the given course instance. A default certificate configuration requires only one course module to be completed.", "operationId": "getCourseInstanceDefaultCertificateConfigurations", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Default certificate configurations", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CertificateConfigurationAndRequirements" } } } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/delete": { "post": { "tags": ["course-instances"], "summary": "\nPOST /course-instances/:id/delete", "operationId": "deleteCourseInstance", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course instance deleted" } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/edit": { "post": { "tags": ["course-instances"], "summary": "\nPOST /course-instances/:id/edit", "operationId": "editCourseInstance", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseInstanceForm" } } }, "required": true }, "responses": { "200": { "description": "Course instance updated" } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/email-templates": { "get": { "tags": ["course-instances"], "operationId": "getCourseInstanceEmailTemplates", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course instance email templates", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/EmailTemplate" } } } } } } }, "post": { "tags": ["course-instances"], "operationId": "createCourseInstanceEmailTemplate", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailTemplateNew" } } }, "required": true }, "responses": { "200": { "description": "Created email template", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailTemplate" } } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/export-completions": { "get": { "tags": ["course-instances"], "summary": "\nGET /course-instances/:id/export-completions - gets CSV of course completion based on course_instance ID.", "operationId": "exportCourseInstanceCompletionsCsv", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course instance completions CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/export-points": { "get": { "tags": ["course-instances"], "summary": "\nGET `/api/v0/main-frontend/course-instances/${courseInstanceId}/export-points` - gets CSV of course instance points based on course_instance ID.", "operationId": "exportCourseInstancePointsCsv", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course instance points CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/points": { "get": { "tags": ["course-instances"], "operationId": "getCourseInstancePoints", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Course instance points", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Points" } } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/progress/{user_id}": { "get": { "tags": ["course-instances"], "summary": "\n GET /api/v0/main-frontend/course-instance/:course_instance_id/progress/:user_id - returns user progress information.", "operationId": "getCourseInstanceUserProgress", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User progress for course instance", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/course-instances/{course_instance_id}/status-for-all-exercises/{user_id}": { "get": { "tags": ["course-instances"], "summary": "\nGET /course-instances/:id/status-for-all-exercises/:user_id - Returns a status for all exercises in a course instance for a given user.", "operationId": "getCourseInstanceExerciseStatusesForUser", "parameters": [ { "name": "course_instance_id", "in": "path", "description": "Course instance id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise statuses for user", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/course-modules/{course_module_id}": { "get": { "tags": ["course_modules"], "summary": "\nGET `/api/v0/main-frontend/course-modules/{course_module_id}`\n\nReturns information about the course module.", "operationId": "getCourseModule", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course module", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseModule" } } } } } } }, "/api/v0/main-frontend/course-modules/{course_module_id}/completion-registration-link": { "get": { "tags": ["course_modules"], "summary": "\nGET `/api/v0/main-frontend/course-modules/{course_slug}/completion-registration-link`", "operationId": "getCourseModuleCompletionRegistrationLink", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Completion registration link", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompletionRegistrationLink" } } } } } } }, "/api/v0/main-frontend/course-modules/{course_module_id}/course-module-completion": { "get": { "tags": ["course_modules"], "summary": "\nGET `/api/v0/main-frontend/course-modules/{course_module_id}/course-module-completion`\n\nGets users's best completion for the course.", "operationId": "getCourseModuleCompletion", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Best course module completion for the current user", "content": { "application/json": { "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CourseModuleCompletion" } ] } } } } } } }, "/api/v0/main-frontend/course-modules/{course_module_id}/set-certificate-generation/{enabled}": { "post": { "tags": ["course_modules"], "operationId": "setCourseModuleCertificateGeneration", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "enabled", "in": "path", "description": "Whether certificate generation should be enabled", "required": true, "schema": { "type": "boolean" } } ], "responses": { "200": { "description": "Certificate generation updated", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/course-modules/{course_module_id}/threshold": { "post": { "tags": ["course_modules"], "summary": "\n POST /api/v0/main-frontend/course-modules/${course_module_id}/threshold - post threshold for a specific course module.", "operationId": "createCourseModuleThreshold", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ThresholdData" } } }, "required": true }, "responses": { "200": { "description": "Threshold created" } } }, "delete": { "tags": ["course_modules"], "summary": "\n DELETE /api/v0/main-frontend/course-modules/${course_module_id}/threshold - delete threshold for a specific course module.", "operationId": "deleteCourseModuleThreshold", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Threshold deleted" } } } }, "/api/v0/main-frontend/course-modules/{course_module_id}/user-completion": { "get": { "tags": ["course_modules"], "summary": "\nGET `/api/v0/main-frontend/course-modules/{course_module_id}/user-completion`\n\nGets active users's completion for the course, if it exists.", "operationId": "getCourseModuleUserCompletion", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User completion information", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserCompletionInformation" } } } } } } }, "/api/v0/main-frontend/course-plans": { "get": { "tags": ["course-plans"], "operationId": "getCourseDesignerPlans", "responses": { "200": { "description": "Plans", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseDesignerPlanSummary" } } } } } } }, "post": { "tags": ["course-plans"], "operationId": "createCourseDesignerPlan", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCourseDesignerPlanRequest" } } }, "required": true }, "responses": { "200": { "description": "Created plan", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlan" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}": { "get": { "tags": ["course-plans"], "operationId": "getCourseDesignerPlan", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Plan details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlanDetails" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/members": { "get": { "tags": ["course-plans"], "operationId": "getCoursePlanMembers", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Plan members", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PlanMemberWithDetails" } } } } } } }, "post": { "tags": ["course-plans"], "operationId": "addCoursePlanMember", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AddPlanMemberRequest" } } }, "required": true }, "responses": { "200": { "description": "Added member", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlanMemberWithDetails" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/members/{user_id}": { "delete": { "tags": ["course-plans"], "operationId": "removeCoursePlanMember", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id to remove", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "204": { "description": "Member removed" } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/schedule": { "put": { "tags": ["course-plans"], "operationId": "saveCourseDesignerSchedule", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SaveCourseDesignerScheduleRequest" } } }, "required": true }, "responses": { "200": { "description": "Updated plan details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlanDetails" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/schedule/finalize": { "post": { "tags": ["course-plans"], "operationId": "finalizeCourseDesignerSchedule", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Finalized plan", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlan" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/schedule/suggestions": { "post": { "tags": ["course-plans"], "operationId": "createCourseDesignerScheduleSuggestion", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerScheduleSuggestionRequest" } } }, "required": true }, "responses": { "200": { "description": "Suggested schedule", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerScheduleSuggestionResponse" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/stages/advance": { "post": { "tags": ["course-plans"], "operationId": "advanceCourseDesignerStage", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Updated plan details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlanDetails" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/stages/{stage_id}/tasks": { "post": { "tags": ["course-plans"], "operationId": "createCourseDesignerStageTask", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "stage_id", "in": "path", "description": "Stage id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCourseDesignerStageTaskRequest" } } }, "required": true }, "responses": { "200": { "description": "Created task", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlanStageTask" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/stages/{stage}/extend": { "post": { "tags": ["course-plans"], "operationId": "extendCourseDesignerStage", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "stage", "in": "path", "description": "Stage name", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExtendStageRequest" } } }, "required": true }, "responses": { "200": { "description": "Updated plan details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlanDetails" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/stages/{stage}/workspace": { "patch": { "tags": ["course-plans"], "operationId": "updateCourseDesignerStageWorkspace", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "stage", "in": "path", "description": "Stage name", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerStageWorkspace" } } }, "required": true }, "responses": { "200": { "description": "Updated plan details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlanDetails" } } } } } } }, "/api/v0/main-frontend/course-plans/{plan_id}/tasks/{task_id}": { "delete": { "tags": ["course-plans"], "operationId": "deleteCourseDesignerStageTask", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "task_id", "in": "path", "description": "Task id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "204": { "description": "Task deleted" } } }, "patch": { "tags": ["course-plans"], "operationId": "updateCourseDesignerStageTask", "parameters": [ { "name": "plan_id", "in": "path", "description": "Plan id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "task_id", "in": "path", "description": "Task id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateCourseDesignerStageTaskRequest" } } }, "required": true }, "responses": { "200": { "description": "Updated task", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDesignerPlanStageTask" } } } } } } }, "/api/v0/main-frontend/courses": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses` - Create a new course.\n# Example\n\nRequest:\n```http\nPOST /api/v0/main-frontend/courses HTTP/1.1\nContent-Type: application/json\n\n{\n \"name\": \"Introduction to introduction\",\n \"slug\": \"introduction-to-introduction\",\n \"organization_id\": \"1b89e57e-8b57-42f2-9fed-c7a6736e3eec\"\n}\n```", "operationId": "createCourse", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewCourse" } } }, "required": true }, "responses": { "200": { "description": "Created course", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Course" } } } } } } }, "/api/v0/main-frontend/courses/": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses` - Get all courses\n\nReturns all courses.", "operationId": "getAllCourses", "responses": { "200": { "description": "All courses", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Course" } } } } } } } }, "/api/v0/main-frontend/courses/join/{join_code}": { "get": { "tags": ["courses"], "summary": "\nGET /courses/join/:join_code - Gets the course related to join code", "operationId": "getCourseByJoinCode", "parameters": [ { "name": "join_code", "in": "path", "description": "Course join code", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Course for join code", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Course" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:course_id` - Get course.", "operationId": "getCourse", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Course" } } } } } }, "put": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/:course_id` - Update course.\n# Example\n\nRequest:\n```http\nPUT /api/v0/main-frontend/courses/ab4541d8-6db4-4561-bdb2-45f35b2544a1 HTTP/1.1\nContent-Type: application/json\n\n{\n \"name\": \"Introduction to Introduction\"\n}\n\n```", "operationId": "updateCourse", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseUpdate" } } }, "required": true }, "responses": { "200": { "description": "Updated course", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Course" } } } } } }, "delete": { "tags": ["courses"], "summary": "\nDELETE `/api/v0/main-frontend/courses/:course_id` - Delete a course.", "operationId": "deleteCourse", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted course", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/courses/{course_id}/breadcrumb-info": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:course_id/breadcrumb-info` - Get information to display breadcrumbs on the manage course pages.", "operationId": "getCourseBreadcrumbInfo", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course breadcrumb information", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseBreadcrumbInfo" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/chatbots": { "get": { "tags": ["courses"], "summary": "GET `/api/v0/main-frontend/courses/{course_id}/chatbots`", "operationId": "getCourseChatbots", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Course chatbots", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ChatbotConfiguration" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/chatbots/{chatbot_configuration_id}/set-as-default": { "post": { "tags": ["courses"], "summary": "POST `/api/v0/main-frontend/courses/{course_id}/chatbots/{chatbot_configuration_id}/set-as-default`", "operationId": "setCourseChatbotAsDefault", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string" } }, { "name": "chatbot_configuration_id", "in": "path", "description": "Chatbot configuration id", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Updated course chatbot", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatbotConfiguration" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/chatbots/{chatbot_configuration_id}/set-as-non-default": { "post": { "tags": ["courses"], "summary": "POST `/api/v0/main-frontend/courses/{course_id}/chatbots/{chatbot_configuration_id}/set-as-non-default`", "operationId": "setCourseChatbotAsNonDefault", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string" } }, { "name": "chatbot_configuration_id", "in": "path", "description": "Chatbot configuration id", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Updated course chatbot", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatbotConfiguration" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/course-instances": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/course-instances` - Returns all course instances for given course id.", "operationId": "getCourseInstances", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course instances", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseInstance" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/course-module-completions/{user_id}": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:course_id/course-module-completions/:user_id` - Returns all course module completions for a given user for this course.", "operationId": "getCourseModuleCompletionsForUser", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course module completions for course user", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModuleCompletion" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/course-modules": { "post": { "tags": ["courses"], "operationId": "updateCourseModules", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ModuleUpdates" } } }, "required": true }, "responses": { "200": { "description": "Course modules updated" } } } }, "/api/v0/main-frontend/courses/{course_id}/course-users-counts-by-exercise": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/course-users-counts-by-exercise` - Returns the amount of users for each exercise.", "operationId": "getCourseUsersCountsByExercise", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course users counts by exercise", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseUserCounts" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/create-copy": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/:id/create-copy` - Create a copy of a course with specified mode.\n\nDifferent copy modes:\n- `duplicate`: Creates a completely separate copy with new language group\n- `same_language_group`: Creates a new language version within the same language group\n- `existing_language_group`: Creates a new language version in the specified language group\n- `new_language_group`: Creates a new language version in a new language group\n\n# Example\n\nRequest:\n```http\nPOST /api/v0/main-frontend/courses/fd484707-25b6-4c51-a4ff-32d8259e3e47/create-copy HTTP/1.1\nContent-Type: application/json\n\n{\n \"name\": \"Johdatus kaikkeen\",\n \"slug\": \"johdatus-kaikkeen\",\n \"organization_id\": \"1b89e57e-8b57-42f2-9fed-c7a6736e3eec\",\n \"language_code\": \"fi-FI\",\n \"mode\": \"duplicate\"\n}\n```\n\nOr with an existing language group:\n```http\nPOST /api/v0/main-frontend/courses/fd484707-25b6-4c51-a4ff-32d8259e3e47/create-copy HTTP/1.1\nContent-Type: application/json\n\n{\n \"name\": \"Johdatus kaikkeen\",\n \"slug\": \"johdatus-kaikkeen\",\n \"organization_id\": \"1b89e57e-8b57-42f2-9fed-c7a6736e3eec\",\n \"language_code\": \"fi-FI\",\n \"mode\": {\n \"existing_language_group\": {\n \"target_course_id\": \"1b89e57e-8b57-42f2-9fed-c7a6736e3eec\"\n }\n }\n}\n```", "operationId": "createCourseCopy", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CopyCourseRequest" } } }, "required": true }, "responses": { "200": { "description": "Created course copy", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Course" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/daily-submission-counts": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/daily-submission-counts` - Returns submission counts grouped by day.", "operationId": "getCourseDailySubmissionCounts", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course daily submission counts", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmissionCount" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/daily-users-who-have-submitted-something": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/daily-users-who-have-submitted-something` - Returns a count of users who have submitted something grouped by day.", "operationId": "getCourseDailyUsersWhoSubmittedSomething", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course daily user submission counts", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmissionCount" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/default-peer-review": { "get": { "tags": ["courses"], "operationId": "getCourseDefaultPeerReview", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Default peer review configuration", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/courses/{course_id}/exercises": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/exercises` - Returns all exercises for the course.", "operationId": "getCourseExercises", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercises for course", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Exercise" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/exercises-and-count-of-answers-requiring-attention": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/exercises-and-count-of-answers-requiring-attention` - Returns all exercises for the course and count of answers requiring attention in them.", "operationId": "getCourseExercisesAndAnswersRequiringAttentionCounts", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercises and answer attention counts", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseAnswersInCourseRequiringAttentionCount" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/export-course-instances": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/export-course-instances`\n\ngets SCV course instances for course", "operationId": "exportCourseInstancesCsv", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course instances CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/export-course-user-consents": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/export-course-user-consents`\n\ngets SCV course specific research form questions and user answers for course", "operationId": "exportCourseUserConsentsCsv", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course user consents CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/export-exercise-tasks": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/export-exercise-tasks`\n\ngets SCV all exercise-tasks' private specs in course", "operationId": "exportCourseExerciseTasksCsv", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course exercise tasks CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/export-submissions": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${courseId}/export-submissions`\n\ngets SCV of course exercise submissions", "operationId": "exportCourseSubmissionsCsv", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course submissions CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/export-user-details": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/export-user-details`\n\ngets SCV of user details for all users having submitted an exercise in the course", "operationId": "exportCourseUserDetailsCsv", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course user details CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/export-user-exercise-states": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/export-user-exercise-states`\n\ngets CSV for course specific user exercise states", "operationId": "exportCourseUserExerciseStatesCsv", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course user exercise states CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/feedback": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/feedback?read=true` - Returns feedback for the given course.", "operationId": "getCourseFeedback", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string" } }, { "name": "read", "in": "query", "description": "Whether to fetch read feedback", "required": true, "schema": { "type": "boolean" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Feedback for the course", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Feedback" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/feedback-count": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/feedback-count` - Returns the amount of feedback for the given course.", "operationId": "getCourseFeedbackCount", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Feedback counts for the course", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FeedbackCount" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/get-course-audiences": { "get": { "tags": ["courses"], "summary": "\nget `/api/v0/main-frontend/courses/:course_id/get-course-audiences` - Get course audiences.", "operationId": "getCourseAudiences", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course audiences", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseAudience" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/get-course-metadata": { "get": { "tags": ["courses"], "summary": "\nget `/api/v0/main-frontend/courses/:course_id/get-course-metadata` - Get course metadata.", "operationId": "getCourseMetadata", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course metadata", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CompleteCourseMetadata" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/get-course-prerequisites": { "get": { "tags": ["courses"], "summary": "\nget `/api/v0/main-frontend/courses/:course_id/get-course-prerequisites` - Get course prerequisites.", "operationId": "getCoursePrerequisites", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course prerequisites", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CoursePrerequisite" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/glossary": { "get": { "tags": ["glossary"], "operationId": "getCourseGlossary", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Glossary terms for the course", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Term" } } } } }, "401": { "description": "Authentication required" }, "403": { "description": "User is not allowed to manage the course glossary" } } }, "post": { "tags": ["glossary"], "operationId": "createCourseGlossaryTerm", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TermUpdate" } } }, "required": true }, "responses": { "200": { "description": "Created glossary term id", "content": { "text/plain": { "schema": { "type": "string", "format": "uuid" } } } }, "401": { "description": "Authentication required" }, "403": { "description": "User is not allowed to manage the course glossary" } } } }, "/api/v0/main-frontend/courses/{course_id}/join-course-with-join-code": { "post": { "tags": ["courses"], "summary": "\nPOST /courses/:course_id/join-course-with-join-code - Adds the user to join_code_uses so the user gets access to the course", "operationId": "joinCourseWithJoinCode", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JoinCourseWithJoinCodePayload" } } }, "required": true }, "responses": { "200": { "description": "Joined course id", "content": { "text/plain": { "schema": { "type": "string", "format": "uuid" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/language-versions": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/language-versions` - Returns all language versions of the same course.\n\n# Example\n\nRequest:\n```http\nGET /api/v0/main-frontend/courses/fd484707-25b6-4c51-a4ff-32d8259e3e47/language-versions HTTP/1.1\nContent-Type: application/json\n```", "operationId": "getCourseLanguageVersions", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course language versions", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Course" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/new-chapter-ordering": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/:id/new-chapter-ordering` - Reorders chapters based on modified chapter number.#\n\nCreates redirects if url_path changes.", "operationId": "updateCourseChapterOrdering", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Chapter" } } } }, "required": true }, "responses": { "200": { "description": "Course chapter ordering updated" } } } }, "/api/v0/main-frontend/courses/{course_id}/new-course-instance": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/:id/new-course-instance`", "operationId": "createCourseInstance", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseInstanceForm" } } }, "required": true }, "responses": { "200": { "description": "Created course instance id", "content": { "text/plain": { "schema": { "type": "string", "format": "uuid" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/new-page-ordering": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/:id/new-page-ordering` - Reorders pages to the given order numbers and given chapters.\n\nNote that the page objects posted here might have the content omitted because it is not needed here and the content makes the request body to be very large.\n\nCreates redirects if url_path changes.", "operationId": "updateCoursePageOrdering", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Page" } } } }, "required": true }, "responses": { "200": { "description": "Course page ordering updated" } } } }, "/api/v0/main-frontend/courses/{course_id}/page-visit-datum-summary": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/page-visit-datum-summary` - Gets aggregated statistics for page visits for the course.", "operationId": "getCoursePageVisitDatumSummary", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course page visit summary", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PageVisitDatumSummaryByCourse" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/page-visit-datum-summary-by-countries": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/page-visit-datum-summary-by-countries` - Gets aggregated statistics for page visits for the course.", "operationId": "getCoursePageVisitDatumSummaryByCountries", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course page visit summary by countries", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PageVisitDatumSummaryByCoursesCountries" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/page-visit-datum-summary-by-device-types": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/page-visit-datum-summary-by-device-types` - Gets aggregated statistics for page visits for the course.", "operationId": "getCoursePageVisitDatumSummaryByDeviceTypes", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course page visit summary by device types", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PageVisitDatumSummaryByCourseDeviceTypes" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/page-visit-datum-summary-by-pages": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/${course.id}/page-visit-datum-summary-by-pages` - Gets aggregated statistics for page visits for the course.", "operationId": "getCoursePageVisitDatumSummaryByPages", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course page visit summary by pages", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PageVisitDatumSummaryByPages" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/partners-block": { "get": { "tags": ["courses"], "summary": "\nGET /courses/:course_id/partners_blocks - Gets a partners block related to a course", "operationId": "getCoursePartnersBlock", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Partners block", "content": { "application/json": { "schema": {} } } } } }, "post": { "tags": ["courses"], "summary": "\n POST /api/v0/main-frontend/courses/:course_id/partners_block - Create or updates a partners block for a course", "operationId": "upsertCoursePartnersBlock", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": {} } } }, "responses": { "200": { "description": "Partners block", "content": { "application/json": { "schema": {} } } } } }, "delete": { "tags": ["courses"], "summary": "\nDELETE `/api/v0/main-frontend/courses/:course_id` - Delete a partners block in a course.", "operationId": "deleteCoursePartnersBlock", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted partners block", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/courses/{course_id}/progress/{user_id}": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:course_id/progress/:user_id` - Returns user progress for the course.", "operationId": "getCourseProgressForUser", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User progress for course", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserCourseProgress" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/references": { "get": { "tags": ["courses"], "operationId": "getCourseReferences", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course references", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/MaterialReference" } } } } } } }, "post": { "tags": ["courses"], "operationId": "createCourseReferences", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/NewMaterialReference" } } } }, "required": true }, "responses": { "200": { "description": "Course references created" } } } }, "/api/v0/main-frontend/courses/{course_id}/references/{reference_id}": { "post": { "tags": ["courses"], "operationId": "updateCourseReference", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "reference_id", "in": "path", "description": "Reference id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewMaterialReference" } } }, "required": true }, "responses": { "200": { "description": "Course reference updated" } } }, "delete": { "tags": ["courses"], "operationId": "deleteCourseReference", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "reference_id", "in": "path", "description": "Reference id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course reference deleted" } } } }, "/api/v0/main-frontend/courses/{course_id}/reprocess-completions": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/{course_id}/reprocess-completions`\n\nReprocesses all module completions for the given course instance. Only available to admins.", "operationId": "reprocessCourseCompletions", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course completions reprocessed", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/set-join-code": { "post": { "tags": ["courses"], "summary": "\n POST /api/v0/main-frontend/courses/:course_id/generate-join-code - Generates a code that is used as a part of URL to join course", "operationId": "setCourseJoinCode", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course join code set" } } } }, "/api/v0/main-frontend/courses/{course_id}/sisu-course-llm-descriptions": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:course_id/sisu-course-llm-descriptions` - Get Sisu descriptions summarised by LLM\n\nReturns LLM generated descriptions for a course based on information from Sisu API.", "operationId": "getSisuCourseLlmDescriptions", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Sisu course LLM descriptions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SisuDescriptionResponse" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/all-language-versions/completions-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/all-language-versions/completions-history/{granularity}/{time_window}`", "description": "Returns completion statistics for all language versions with specified time granularity and window.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getCourseCompletionsHistoryAllLanguageVersions", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Course completions history across all language versions", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/all-language-versions/total-users-started": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/all-language-versions/total-users-started`", "operationId": "getTotalUsersStartedAllLanguageVersions", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Total users started across all language versions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CountResult" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/all-language-versions/users-starting-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/all-language-versions/users-starting-history/{granularity}/{time_window}`", "description": "Returns unique users starting statistics for all language versions with specified time granularity and window.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getUniqueUsersStartingHistoryAllLanguageVersions", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Unique users starting history across all language versions", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/avg-time-to-first-submission/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/avg-time-to-first-submission/{granularity}/{time_window}`", "description": "Returns average time to first submission statistics with specified time granularity and window.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getAvgTimeToFirstSubmissionHistory", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Average time to first submission history", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/AverageMetric" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/by-instance/completions-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/by-instance/completions-history/{granularity}/{time_window}`", "description": "Returns course completion statistics with specified time granularity and window, grouped by course instance.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getCourseCompletionsHistoryByInstance", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Course completions history by instance", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } }, "propertyNames": { "type": "string", "format": "uuid" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/by-instance/first-submissions-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/by-instance/first-submissions-history/{granularity}/{time_window}`", "description": "Returns first exercise submission statistics with specified time granularity and window, grouped by course instance.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getFirstExerciseSubmissionsHistoryByInstance", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "First exercise submissions history by instance", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } }, "propertyNames": { "type": "string", "format": "uuid" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/by-instance/total-users-completed": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/by-instance/total-users-completed`", "operationId": "getTotalUsersCompletedCourseByInstance", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Total users completed course by instance", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/CountResult" }, "propertyNames": { "type": "string", "format": "uuid" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/by-instance/total-users-returned-exercises": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/by-instance/total-users-returned-exercises`", "operationId": "getTotalUsersReturnedExercisesByInstance", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Total users returned exercises by instance", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/CountResult" }, "propertyNames": { "type": "string", "format": "uuid" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/by-instance/total-users-started-course": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/by-instance/total-users-started-course`", "operationId": "getTotalUsersStartedCourseByInstance", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Total users started course by instance", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/CountResult" }, "propertyNames": { "type": "string", "format": "uuid" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/by-instance/users-returning-exercises-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/by-instance/users-returning-exercises-history/{granularity}/{time_window}`", "description": "Returns users returning exercises statistics with specified time granularity and window, grouped by course instance.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getUsersReturningExercisesHistoryByInstance", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Users returning exercises history by instance", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } }, "propertyNames": { "type": "string", "format": "uuid" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/by-instance/users-starting-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/by-instance/users-starting-history/{granularity}/{time_window}`", "description": "Returns unique users starting statistics with specified time granularity and window, grouped by course instance.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getUniqueUsersStartingHistoryByInstance", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Unique users starting history by instance", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } }, "propertyNames": { "type": "string", "format": "uuid" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/by-module/first-submissions/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/by-module/first-submissions/{granularity}/{time_window}`", "description": "Returns first exercise submission statistics with specified time granularity and window,\ngrouped by module.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getFirstExerciseSubmissionsByModule", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "First exercise submissions by module", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } }, "propertyNames": { "type": "string", "format": "uuid" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/cohort-activity/{granularity}/{history_window}/{tracking_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/cohort-activity/{granularity}/{history_window}/{tracking_window}`", "operationId": "getCohortActivityHistory", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "history_window", "in": "path", "description": "History window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "tracking_window", "in": "path", "description": "Tracking window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Cohort activity history", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CohortActivity" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/completions-history/custom-time-period/{start_date}/{end_date}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/completions-history/custom-time-period/{start_date}/{end_date}`", "description": "Returns completion statistics by custom time period.\nQuery parameters:\n- start_date: YYYY-MM-DD\n- end_date: YYYY-MM-DD", "operationId": "getCourseCompletionsHistoryCustomTimePeriod", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "start_date", "in": "path", "description": "Start date", "required": true, "schema": { "type": "string" } }, { "name": "end_date", "in": "path", "description": "End date", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Course completions history for custom time period", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/completions-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/completions-history/{granularity}/{time_window}`", "description": "Returns completion statistics for the course with specified time granularity and window.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getCourseCompletionsHistory", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Course completions history", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/first-submissions-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/first-submissions-history/{granularity}/{time_window}`", "description": "Returns first exercise submission statistics with specified time granularity and window.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getFirstExerciseSubmissionsHistory", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "First exercise submissions history", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/student-completions-by-country/{granularity}/{time_window}/{country}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/student-completions-by-country/{granularity}/{time_window}/{country}`", "description": "Returns student completion statistics grouped by country with the specified time granularity.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getStudentCompletionsByCountry", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "country", "in": "path", "description": "Country", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Student completions by country", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/student-enrollments-by-country/{granularity}/{time_window}/{country}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/student-enrollments-by-country/{granularity}/{time_window}/{country}`", "description": "Returns student signup statistics grouped by country with the specified time granularity.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getStudentEnrollmentsByCountry", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "country", "in": "path", "description": "Country", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Student enrollments by country", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/students-by-country-totals": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/students-by-country-totals`", "description": "Returns all enrolled students grouped by country.", "operationId": "getStudentsByCountryTotals", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Students by country totals", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/StudentsByCountryTotalsResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/total-users-completed": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/total-users-completed`", "operationId": "getTotalUsersCompletedCourse", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Total users completed course", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CountResult" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/total-users-completed/custom-time-period/{start_date}/{end_date}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/total-users-completed/custom-time-period/{start_date}/{end_date}`", "operationId": "getTotalUsersCompletedCourseCustomTimePeriod", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "start_date", "in": "path", "description": "Start date", "required": true, "schema": { "type": "string" } }, { "name": "end_date", "in": "path", "description": "End date", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Total users completed course for custom time period", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CountResult" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/total-users-returned-exercises": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/total-users-returned-exercises`", "operationId": "getTotalUsersReturnedExercises", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Total users returned exercises", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CountResult" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/total-users-returned-exercises/custom-time-period/{start_date}/{end_date}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/total-users-returned-exercises/custom-time-period/{start_date}/{end_date}`", "operationId": "getTotalUsersReturnedExercisesCustomTimePeriod", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "start_date", "in": "path", "description": "Start date", "required": true, "schema": { "type": "string" } }, { "name": "end_date", "in": "path", "description": "End date", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Total users returned exercises for custom time period", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CountResult" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/total-users-started-course": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/total-users-started-course`", "operationId": "getTotalUsersStartedCourse", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Total users started course", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CountResult" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/total-users-started-course/custom-time-period/{start_date}/{end_date}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/total-users-started-course/custom-time-period/{start_date}/{end_date}`", "operationId": "getTotalUsersStartedCourseCustomTimePeriod", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "start_date", "in": "path", "description": "Start date", "required": true, "schema": { "type": "string" } }, { "name": "end_date", "in": "path", "description": "End date", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Total users started course for custom time period", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CountResult" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/users-returning-exercises-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/users-returning-exercises-history/{granularity}/{time_window}`", "description": "Returns users returning exercises statistics with specified time granularity and window.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getUsersReturningExercisesHistory", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Users returning exercises history", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/users-starting-history/custom-time-period/{start_date}/{end_date}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/users-starting-history/custom-time-period/{start_date}/{end_date}`", "description": "Returns unique users starting statistics with specified time period.", "operationId": "getUniqueUsersStartingHistoryCustomTimePeriod", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "start_date", "in": "path", "description": "Start date", "required": true, "schema": { "type": "string" } }, { "name": "end_date", "in": "path", "description": "End date", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Unique users starting history for custom time period", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/stats/users-starting-history/{granularity}/{time_window}": { "get": { "tags": ["course-stats"], "summary": "GET `/api/v0/main-frontend/{course_id}/stats/users-starting-history/{granularity}/{time_window}`", "description": "Returns unique users starting statistics with specified time granularity and window.\n- granularity: \"year\", \"month\", or \"day\"\n- time_window: number of time units to look back", "operationId": "getUniqueUsersStartingHistory", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "granularity", "in": "path", "description": "Time granularity", "required": true, "schema": { "$ref": "#/components/schemas/TimeGranularity" } }, { "name": "time_window", "in": "path", "description": "Time window", "required": true, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Unique users starting history", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CountResult" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/status-for-all-exercises/{user_id}": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:course_id/status-for-all-exercises/:user_id` - Returns status for all exercises in the course for a given user.", "operationId": "getCourseExerciseStatusesForUser", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise statuses for course user", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseStatusSummaryForUser" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/structure": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:course_id/structure` - Returns the structure of a course.\n# Example\n```json\n{\n \"course\": {\n \"id\": \"d86cf910-4d26-40e9-8c9c-1cc35294fdbb\",\n \"slug\": \"introduction-to-everything\",\n \"created_at\": \"2021-04-28T10:40:54.503917\",\n \"updated_at\": \"2021-04-28T10:40:54.503917\",\n \"name\": \"Introduction to everything\",\n \"organization_id\": \"1b89e57e-8b57-42f2-9fed-c7a6736e3eec\",\n \"deleted_at\": null,\n \"language_code\": \"en-US\",\n \"copied_from\": null,\n \"language_version_of_course_id\": null\n },\n \"pages\": [\n {\n \"id\": \"f3b0d699-c9be-4d56-bd0a-9d40e5547e4d\",\n \"created_at\": \"2021-04-28T13:51:51.024118\",\n \"updated_at\": \"2021-04-28T14:36:18.179490\",\n \"course_id\": \"d86cf910-4d26-40e9-8c9c-1cc35294fdbb\",\n \"content\": [],\n \"url_path\": \"/\",\n \"title\": \"Welcome to Introduction to Everything\",\n \"deleted_at\": null,\n \"chapter_id\": \"d332f3d9-39a5-4a18-80f4-251727693c37\"\n }\n ],\n \"chapters\": [\n {\n \"id\": \"d332f3d9-39a5-4a18-80f4-251727693c37\",\n \"created_at\": \"2021-04-28T16:11:47.477850\",\n \"updated_at\": \"2021-04-28T16:11:47.477850\",\n \"name\": \"The Basics\",\n \"course_id\": \"d86cf910-4d26-40e9-8c9c-1cc35294fdbb\",\n \"deleted_at\": null,\n \"chapter_image_url\": \"http://project-331.local/api/v0/files/uploads/organizations/1b89e57e-8b57-42f2-9fed-c7a6736e3eec/courses/d86cf910-4d26-40e9-8c9c-1cc35294fdbb/images/mbPQh8th96TdUwX96Y0ch1fjbJLRFr.png\",\n \"chapter_number\": 1,\n \"front_page_id\": null\n }\n ]\n}\n```", "operationId": "getCourseStructure", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course structure", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseStructure" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/certificates": { "post": { "tags": ["course-students"], "summary": "POST `/api/v0/main-frontend/courses/{course_id}/students/certificates`", "operationId": "getCourseStudentsCertificates", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserIdsPayload" } } }, "required": true }, "responses": { "200": { "description": "Course certificates for the given users", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CertificateGridRow" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/completions": { "post": { "tags": ["course-students"], "summary": "POST `/api/v0/main-frontend/courses/{course_id}/students/completions`", "operationId": "getCourseStudentsCompletions", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserIdsPayload" } } }, "required": true }, "responses": { "200": { "description": "Course completions for the given users", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CompletionGridRow" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/progress": { "post": { "tags": ["course-students"], "summary": "POST `/api/v0/main-frontend/courses/{course_id}/students/progress`", "operationId": "getCourseStudentsProgress", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserIdsPayload" } } }, "required": true }, "responses": { "200": { "description": "Per-user course progress for the given users", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseStudentsProgressUsers" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/progress-structure": { "get": { "tags": ["course-students"], "summary": "GET `/api/v0/main-frontend/courses/{course_id}/students/progress-structure`", "operationId": "getCourseStudentsProgressStructure", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course-level progress structure", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseStudentsProgressStructure" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/users": { "get": { "tags": ["course-students"], "summary": "GET `/api/v0/main-frontend/courses/{course_id}/students/users`", "operationId": "getCourseStudentsUsers", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number (1-based)", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "limit", "in": "query", "description": "Page size (1-10000)", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "search", "in": "query", "description": "Filter by name/email substring or exact user id", "required": false, "schema": { "type": "string" } }, { "name": "sort_column", "in": "query", "description": "last_name | first_name | email | total_points", "required": false, "schema": { "type": "string" } }, { "name": "sort_direction", "in": "query", "description": "asc | desc", "required": false, "schema": { "type": "string" } }, { "name": "course_instance_id", "in": "query", "description": "Filter to a single course instance", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "module_id", "in": "query", "description": "Scopes `grade` to this module's completions", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "grade", "in": "query", "description": "A sis-0-5 grade (\"0\"..\"5\"), \"passed\"/\"failed\", or \"not_completed\"; requires module_id", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "A page of enrolled students", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StudentsListPage" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/{user_id}/chapter-locking-statuses": { "get": { "tags": ["course-students"], "summary": "GET `/api/v0/main-frontend/courses/{course_id}/students/{user_id}/chapter-locking-statuses`", "operationId": "getCourseStudentChapterLockingStatuses", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "Target student id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Student chapter locking statuses", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserChapterLockingStatus" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/{user_id}/chapters/{chapter_id}/lock": { "post": { "tags": ["course-students"], "summary": "POST `/api/v0/main-frontend/courses/{course_id}/students/{user_id}/chapters/{chapter_id}/lock`", "operationId": "teacherLockStudentChapter", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "Target student id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "chapter_id", "in": "path", "description": "Chapter id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Updated chapter locking status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserChapterLockingStatus" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/{user_id}/chapters/{chapter_id}/status": { "post": { "tags": ["course-students"], "summary": "POST `/api/v0/main-frontend/courses/{course_id}/students/{user_id}/chapters/{chapter_id}/status`", "operationId": "teacherSetStudentChapterStatus", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "Target student id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "chapter_id", "in": "path", "description": "Chapter id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChapterLockStatusActionPayload" } } }, "required": true }, "responses": { "200": { "description": "Updated chapter locking status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserChapterLockingStatus" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/students/{user_id}/chapters/{chapter_id}/unlock": { "post": { "tags": ["course-students"], "summary": "POST `/api/v0/main-frontend/courses/{course_id}/students/{user_id}/chapters/{chapter_id}/unlock`", "operationId": "teacherUnlockStudentChapter", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "Target student id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "chapter_id", "in": "path", "description": "Chapter id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Updated chapter locking status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserChapterLockingStatus" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/submission-counts-by-exercise": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/submission-counts-by-exercise` - Returns submission counts grouped by weekday and hour.", "operationId": "getCourseSubmissionCountsByExercise", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course submission counts by exercise", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmissionCountByExercise" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/suspected-cheaters": { "get": { "tags": ["courses"], "summary": "\n GET /api/v0/main-frontend/courses/${course.id}/suspected-cheaters?status=Flagged - returns the suspected cheaters in the given review state for a course.", "operationId": "getCourseSuspectedCheaters", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "status", "in": "query", "description": "Which review state of suspected cheaters to fetch", "required": true, "schema": { "$ref": "#/components/schemas/SuspectedCheaterStatus" } } ], "responses": { "200": { "description": "Suspected cheaters for course", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/SuspectedCheaters" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/suspected-cheaters/confirm/{user_id}": { "post": { "tags": ["courses"], "summary": "\n POST /api/v0/main-frontend/courses/${course.id}/suspected-cheaters/confirm/:user_id - confirms the student cheated (sets status to 'ConfirmedCheating') and fails the student.", "operationId": "confirmCourseSuspectedCheater", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "Suspected cheater's user id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Cheating confirmed" } } } }, "/api/v0/main-frontend/courses/{course_id}/suspected-cheaters/dismiss/{user_id}": { "post": { "tags": ["courses"], "summary": "\n POST /api/v0/main-frontend/courses/${course.id}/suspected-cheaters/dismiss/:user_id - dismisses the suspicion as a false alarm (sets status to 'Dismissed').", "operationId": "dismissCourseSuspectedCheater", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "Suspected cheater's user id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Suspicion dismissed" } } } }, "/api/v0/main-frontend/courses/{course_id}/suspected-cheaters/flagged-count": { "get": { "tags": ["courses"], "summary": "\n GET /api/v0/main-frontend/courses/${course.id}/suspected-cheaters/flagged-count - number of suspected cheaters awaiting review (status flagged). Used to show a review notification to teachers.", "operationId": "getCourseFlaggedSuspectedCheatersCount", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Number of suspected cheaters awaiting review", "content": { "application/json": { "schema": { "type": "integer", "format": "int64" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/teacher-reset-course-progress-for-everyone": { "delete": { "tags": ["courses"], "summary": "\nDELETE `/api/v0/main-frontend/courses/${course.id}/teacher-reset-course-progress-for-everyone` - Can be used by teachers to reset the course progress for all students. Only works when the course is a draft and not published to students. Cannot be used to delete a course that some students have taken.\n\nDeletes submissions, user exercise states, and peer reviews etc. for all the course instances of this course.", "operationId": "resetCourseProgressForEveryone", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course progress reset for everyone", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/teacher-reset-course-progress-for-themselves": { "delete": { "tags": ["courses"], "summary": "\nDELETE `/api/v0/main-frontend/courses/${course.id}/teacher-reset-course-progress-for-themselves` - Allows a teacher to reset the course progress for themselves. Cannot be used to reset the course for others.\n\nDeletes submissions, user exercise states, and peer reviews etc. for all the course instances of this course.", "operationId": "resetCourseProgressForTeacherThemselves", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Teacher course progress reset", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/thresholds": { "get": { "tags": ["courses"], "summary": "\n GET /api/v0/main-frontend/courses/${course.id}/thresholds - get all thresholds for all modules in a course.", "operationId": "getCourseThresholds", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course thresholds", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModuleThresholdInfo" } } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/update-metadata": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/:course_id/update-metadata` - Update metadata.", "operationId": "updateMetadata", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseMetadataUpdate" } } }, "required": true }, "responses": { "200": { "description": "Updated metadata", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseMetadata" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/update-peer-review-queue-reviews-received": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/${course_id}/update-peer-review-queue-reviews-received`\n\nUpdates reviews received for all the students in the peer review queue for a specific course. Updates only entries that have not received enough peer reviews in the table. Only available to admins.", "operationId": "updateCoursePeerReviewQueueReviewsReceived", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Peer review queue updated", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/upload": { "post": { "tags": ["courses"], "summary": "\nPOST `/api/v0/main-frontend/courses/:course_id/upload` - Uploads a media (image, audio, file) for the course from Gutenberg page edit.\n\nPut the the contents of the media in a form and add a content type header multipart/form-data.\n# Example\n\nRequest:\n```http\nPOST /api/v0/main-frontend/pages/d86cf910-4d26-40e9-8c9c-1cc35294fdbb/upload HTTP/1.1\nContent-Type: multipart/form-data\n\nBINARY_DATA\n```", "operationId": "uploadCourseMedia", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "multipart/form-data": { "schema": { "type": "string" } } }, "required": true }, "responses": { "200": { "description": "Uploaded media result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadResult" } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/user-settings/{user_id}": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:course_id/user-settings/:user_id` - Get current course settings for a specific user.", "operationId": "getCourseUserSettingsForUser", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User course settings", "content": { "application/json": { "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/UserCourseSettings" } ] } } } } } } }, "/api/v0/main-frontend/courses/{course_id}/weekday-hour-submission-counts": { "get": { "tags": ["courses"], "summary": "\nGET `/api/v0/main-frontend/courses/:id/weekday-hour-submission-counts` - Returns submission counts grouped by weekday and hour.", "operationId": "getCourseWeekdayHourSubmissionCounts", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course weekday and hour submission counts", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmissionCountByWeekAndHour" } } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/account-linking": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/account-linking` - The linking funnel, the\nper-realisation counters, the send-status totals and the stale-address list.", "operationId": "getAccountLinkingStats", "parameters": [ { "name": "window_days", "in": "query", "description": "Window for the windowed funnel steps, in days", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Where account linking stands", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountLinkingStats" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/account-linking/manual-link": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/account-linking/manual-link` - Links a student\nnumber to an account on an admin's judgement.\n\nThe last resort, for a student whose mailbox host will not accept our mail at all. An admin's judgement\nstands in for proof of mailbox control, so the link is marked `admin_manual` forever, carries the\nreason and names the admin.", "operationId": "adminManuallyLinkStudentNumber", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminManuallyLinkStudentNumberPayload" } } }, "required": true }, "responses": { "200": { "description": "What the attempt did", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminManuallyLinkStudentNumberResult" } } } }, "422": { "description": "No reason, no student number, or no person id from the preview" } } } }, "/api/v0/main-frontend/credit-registration-admin/account-linking/resend": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/account-linking/resend` - Sets off another\naccount-linking mail for one person on one course.\n\nThe mail goes to the addresses the study registry holds, and the recipient still has to open the link\nwhile signed in, so the ownership proof is intact. An override does not ask a cap for an exemption: it\nretires the ledger rows the cap is counting, as its own audited action, then runs the ordinary path.", "operationId": "adminResendAccountLinkingEmail", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminResendAccountLinkingEmailPayload" } } }, "required": true }, "responses": { "200": { "description": "What the attempt did", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminResendAccountLinkingEmailResult" } } } }, "422": { "description": "An override without a reason, or too soon after the last resend" } } } }, "/api/v0/main-frontend/credit-registration-admin/account-linking/resolve-person": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/account-linking/resolve-person` - Looks one\nstudent number up in the study registry without changing anything.\n\nThe preview a manual link is gated on. Writes nothing but the call log row every study registry call\nwrites.", "operationId": "adminResolveStudentNumberForLinking", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminResolveStudentNumberPayload" } } }, "required": true }, "responses": { "200": { "description": "Who the study registry says the number belongs to", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminResolveStudentNumberResult" } } } }, "422": { "description": "No student number given" } } } }, "/api/v0/main-frontend/credit-registration-admin/attention": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/attention` - The rows at least one detector\nwants a human to look at, with the detectors that picked each.\n\nSuperseded attempts and rows abandoned by a consent withdrawal are outside every detector: neither\nis something a person can act on.", "operationId": "getCreditRegistrationAttentionItems", "responses": { "200": { "description": "Rows needing a human, and how many for each reason", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationAttentionItems" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/audit": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/audit` - A page of the global action log,\nnewest first.\n\nCovers admin and course-teacher actors alike, and every target kind: a registration, a course\nmodule, a course, a phase, a student-number link or its token.", "operationId": "listCreditRegistrationAdminActions", "parameters": [ { "name": "page", "in": "query", "description": "Page number, from 1", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "limit", "in": "query", "description": "Rows per page", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "action", "in": "query", "description": "Action kinds; repeat the parameter for several", "required": false, "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationAdminAction" } } }, { "name": "actor_user_id", "in": "query", "description": "Who acted", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "actor_role", "in": "query", "description": "global_admin or course_teacher", "required": false, "schema": { "type": "string" } }, { "name": "target_kind", "in": "query", "description": "What was acted on", "required": false, "schema": { "$ref": "#/components/schemas/CreditRegistrationAdminActionTarget" } }, { "name": "target_id", "in": "query", "description": "One target row", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "target_phase", "in": "query", "description": "One pipeline phase", "required": false, "schema": { "type": "string" } }, { "name": "course_id", "in": "query", "description": "Actions on this course, and actions its teachers took", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "from", "in": "query", "description": "Taken at or after", "required": false, "schema": { "type": "string", "format": "date-time" } }, { "name": "to", "in": "query", "description": "Taken at or before", "required": false, "schema": { "type": "string", "format": "date-time" } } ], "responses": { "200": { "description": "A page of the action log", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationAdminActionsPage" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/courses": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/courses` - Every Suotar-enabled course module,\nits validated configuration and its volumes.", "operationId": "getCreditRegistrationStatsByCourse", "responses": { "200": { "description": "One row per enabled course module", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationStatsByCourse" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/courses/{course_module_id}/pause": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/courses/{course_module_id}/pause` - Stops every\nphase from claiming this module's rows.\n\nFreezes the rows where they stand rather than cancelling them, so resuming picks up what was already\nin flight.", "operationId": "adminPauseCourseModuleCreditRegistration", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminPauseCourseModulePayload" } } }, "required": true }, "responses": { "200": { "description": "Paused" }, "422": { "description": "No reason given, or the module has no Suotar configuration" } } } }, "/api/v0/main-frontend/credit-registration-admin/courses/{course_module_id}/resume": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/courses/{course_module_id}/resume` - Lets the\nphases claim this module's rows again.", "operationId": "adminResumeCourseModuleCreditRegistration", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminResumeCourseModulePayload" } } }, "required": true }, "responses": { "200": { "description": "Resumed" }, "422": { "description": "The module has no Suotar configuration" } } } }, "/api/v0/main-frontend/credit-registration-admin/errors/by-code": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/errors/by-code` - Error events per code over a\nwindow and the window before it, with the terminal verdicts of the same window beside them.\n\nCounts events, not rows: an error that happened really happened, whether or not a later attempt\nsucceeded, and hiding it would hide the configuration bug that caused it.", "operationId": "getCreditRegistrationErrorsByCode", "parameters": [ { "name": "window_secs", "in": "query", "description": "Window length in seconds; the same length before it is the comparison", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Per-code counts and the window's verdicts", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationErrorsByCode" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/materialize": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/materialize` - Creates ledger rows for eligible\ncompletions and recomputes preconditions, now.\n\nRuns the two database-only steps directly rather than through the phase dispatcher, because the\nphase-state row describes the worker loops: an admin pressing a button must not make a dead worker look\nalive.", "operationId": "adminMaterializeCreditRegistrations", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminMaterializePayload" } } }, "required": true }, "responses": { "200": { "description": "How many rows were created and moved", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminMaterializeResult" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/overview": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/overview` - Everything the Overview tab and the\nalert banner render, in one request so the tiles cannot contradict each other.", "operationId": "getCreditRegistrationOverview", "responses": { "200": { "description": "Counts, throughput, phase heartbeats and the active alerts", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationOverview" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/phases": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/phases` - Every pipeline phase, its heartbeat\nand the queue it is responsible for.", "operationId": "listCreditRegistrationPhases", "responses": { "200": { "description": "One row per pipeline phase", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationPhaseList" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/phases/{phase}/pause": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/phases/{phase}/pause` - Pauses one phase: the\nworker loop skips it on every tick until it is resumed.", "operationId": "adminPausePhase", "parameters": [ { "name": "phase", "in": "path", "description": "One of the twelve canonical phase names", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminPausePhasePayload" } } }, "required": true }, "responses": { "200": { "description": "The phase's status after pausing", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationPhaseStatus" } } } }, "422": { "description": "No reason given, or not one of the canonical phase names" } } } }, "/api/v0/main-frontend/credit-registration-admin/phases/{phase}/resume": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/phases/{phase}/resume` - Resumes one paused\nphase.", "operationId": "adminResumePhase", "parameters": [ { "name": "phase", "in": "path", "description": "One of the twelve canonical phase names", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminPhaseActionPayload" } } }, "required": true }, "responses": { "200": { "description": "The phase's status after resuming", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationPhaseStatus" } } } }, "422": { "description": "Not one of the canonical phase names" } } } }, "/api/v0/main-frontend/credit-registration-admin/phases/{phase}/run-now": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/phases/{phase}/run-now` - Makes one phase due\nimmediately: the worker loop picks it up on its next tick instead of waiting out `next_run_at`.", "operationId": "adminRunPhaseNow", "parameters": [ { "name": "phase", "in": "path", "description": "One of the twelve canonical phase names", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminPhaseActionPayload" } } }, "required": true }, "responses": { "200": { "description": "The phase's status after being made due", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationPhaseStatus" } } } }, "422": { "description": "Not one of the canonical phase names" } } } }, "/api/v0/main-frontend/credit-registration-admin/pipeline-history": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/pipeline-history` - Daily queue depth per\nledger state, with what entered and left each state that day.", "operationId": "getCreditRegistrationPipelineHistory", "parameters": [ { "name": "days", "in": "query", "description": "How many days back to read, today included", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "One entry per day that has a snapshot", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationHistory" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/reconciliation": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/reconciliation` - The drift detectors: work the\nledger should be doing and is not, and outcomes the study registry and the ledger disagree about.", "operationId": "getCreditRegistrationReconciliation", "responses": { "200": { "description": "Every detector's findings and counts", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationReconciliation" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/registrations": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/registrations` - A page of the ledger, filtered\nand sorted.", "operationId": "listCreditRegistrationsForAdmin", "parameters": [ { "name": "page", "in": "query", "description": "Page number, from 1", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "limit", "in": "query", "description": "Rows per page", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "state", "in": "query", "description": "Ledger states; repeat the parameter for several", "required": false, "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationState" } } }, { "name": "error_code", "in": "query", "description": "Error codes; repeat the parameter for several", "required": false, "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } } }, { "name": "course_id", "in": "query", "description": "Course filter", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "course_module_id", "in": "query", "description": "Course module filter", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "query", "description": "Student filter", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "student_number", "in": "query", "description": "Exact student number, frozen on the row or linked to the account", "required": false, "schema": { "type": "string" } }, { "name": "needs_admin_attention", "in": "query", "description": "Only rows asking for a human", "required": false, "schema": { "type": "boolean" } }, { "name": "submitted_after", "in": "query", "description": "Submitted at or after", "required": false, "schema": { "type": "string", "format": "date-time" } }, { "name": "submitted_before", "in": "query", "description": "Submitted at or before", "required": false, "schema": { "type": "string", "format": "date-time" } }, { "name": "search", "in": "query", "description": "Name, email, student number, attainment id, stored error text, or a uuid", "required": false, "schema": { "type": "string" } }, { "name": "include_superseded", "in": "query", "description": "Include replaced attempts", "required": false, "schema": { "type": "boolean" } }, { "name": "sort", "in": "query", "description": "last_activity, created, time_in_state or attempts", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "A page of the ledger", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminCreditRegistrationsPage" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/registrations/bulk-transition": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/registrations/bulk-transition` - Moves a\nselection of rows by hand, one transaction for the lot.\n\nResubmitting refuses every row in `submission_uncertain`, whatever the selection said. Taking one of\nthose back to `ready_to_submit` is a decision about one student's transcript, made after somebody has\nlooked the attainment up; a checkbox in a list is not that, and a mis-click here would put a second\nattainment on every one of them. Those rows are reported back untouched, to be dealt with one at a\ntime.", "operationId": "adminBulkTransitionCreditRegistrations", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminBulkTransitionPayload" } } }, "required": true }, "responses": { "200": { "description": "What each selected row did", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminBulkTransitionResult" } } } }, "422": { "description": "No reason given, or more ids than one call may take" } } } }, "/api/v0/main-frontend/credit-registration-admin/registrations/requeue-retryable": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/registrations/requeue-retryable` - Makes every\n`failed_retryable` row waiting out a backoff due now.\n\nThe button pressed once the study registry says an outage is over. Touches nothing but\n`next_attempt_at`: the rows are already where the pipeline wants them, they are merely waiting.", "operationId": "adminRequeueRetryableCreditRegistrations", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminRequeueRetryablePayload" } } }, "required": true }, "responses": { "200": { "description": "How many were made due", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminRequeueRetryableResult" } } } }, "422": { "description": "No reason given" } } } }, "/api/v0/main-frontend/credit-registration-admin/registrations/{credit_registration_id}": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/registrations/{credit_registration_id}` - One\nrow with its timeline, the calls that timeline refers to, the other attempts for the same completion,\nthe actions taken on it and its linking mails.", "operationId": "getCreditRegistrationForAdmin", "parameters": [ { "name": "credit_registration_id", "in": "path", "description": "Credit registration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The row and everything that happened to it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminCreditRegistrationDetails" } } } }, "404": { "description": "No such registration" } } } }, "/api/v0/main-frontend/credit-registration-admin/registrations/{credit_registration_id}/transition": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/registrations/{credit_registration_id}/transition`\n- Moves one row by hand.\n\nThe escape hatch out of `submission_uncertain`, which the pipeline never leaves on its own because\nre-importing could put a second attainment on a real transcript. Resubmitting re-checks consent, because\na `misregistered` row sits outside the automatic machinery that would otherwise have checked it.", "operationId": "adminTransitionCreditRegistration", "parameters": [ { "name": "credit_registration_id", "in": "path", "description": "Credit registration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminTransitionCreditRegistrationPayload" } } }, "required": true }, "responses": { "200": { "description": "What the transition did", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminTransitionCreditRegistrationResult" } } } }, "404": { "description": "No such registration" }, "422": { "description": "No reason given" } } } }, "/api/v0/main-frontend/credit-registration-admin/student-numbers": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/student-numbers` - A page of the live links, for\nspot-checking and support.", "operationId": "listVerifiedStudentNumbersForAdmin", "parameters": [ { "name": "page", "in": "query", "description": "Page number, from 1", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "limit", "in": "query", "description": "Rows per page", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "verified_via", "in": "query", "description": "How the link was established", "required": false, "schema": { "$ref": "#/components/schemas/StudentNumberVerificationMethod" } }, { "name": "search", "in": "query", "description": "Student number, name or email", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "A page of the live links", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminVerifiedStudentNumbersPage" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/student-numbers/{verified_student_number_id}/unlink": { "post": { "tags": ["credit-registration-admin"], "summary": "\nPOST `/api/v0/main-frontend/credit-registration-admin/student-numbers/{id}/unlink` - Retires one link.\n\nA reason is required, so the request carries a body rather than being a `DELETE`. The row is\nsoft-deleted: the number a student once held is part of the audit trail.", "operationId": "adminUnlinkStudentNumber", "parameters": [ { "name": "verified_student_number_id", "in": "path", "description": "Verified student number id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminUnlinkStudentNumberPayload" } } }, "required": true }, "responses": { "200": { "description": "How many registrations went back to waiting", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AdminUnlinkStudentNumberResult" } } } }, "404": { "description": "No such link" }, "422": { "description": "No reason given" } } } }, "/api/v0/main-frontend/credit-registration-admin/suotar-api-calls": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/suotar-api-calls` - A page of the study\nregistry call log, newest first.\n\nFiltering by `credit_registration_id` is how \"find the call that carried this student\" is answered:\nthe bodies are scrubbed, so there is no student number in them to search.", "operationId": "listSuotarApiCalls", "parameters": [ { "name": "page", "in": "query", "description": "Page number, from 1", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "limit", "in": "query", "description": "Rows per page", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "endpoint", "in": "query", "description": "One study registry endpoint", "required": false, "schema": { "$ref": "#/components/schemas/SuotarEndpoint" } }, { "name": "succeeded", "in": "query", "description": "Only calls that did, or did not, succeed", "required": false, "schema": { "type": "boolean" } }, { "name": "worker_name", "in": "query", "description": "The phase or manual action that made the call", "required": false, "schema": { "type": "string" } }, { "name": "started_after", "in": "query", "description": "Started at or after", "required": false, "schema": { "type": "string", "format": "date-time" } }, { "name": "started_before", "in": "query", "description": "Started at or before", "required": false, "schema": { "type": "string", "format": "date-time" } }, { "name": "credit_registration_id", "in": "query", "description": "Only calls that carried this ledger row", "required": false, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "A page of the call log", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuotarApiCallsPage" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/suotar-api-calls/{suotar_api_call_id}": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/suotar-api-calls/{suotar_api_call_id}` - One\ncall with its stored bodies, the ledger rows it covered and the timeline entries it produced.", "operationId": "getSuotarApiCall", "parameters": [ { "name": "suotar_api_call_id", "in": "path", "description": "Study registry call id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The call and everything it touched", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuotarApiCallDetails" } } } }, "404": { "description": "No such call" } } } }, "/api/v0/main-frontend/credit-registration-admin/suotar-health": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/suotar-health` - Per-endpoint call counts,\nsuccess rates and latency percentiles over an hour, a day and a week.", "operationId": "getSuotarHealth", "responses": { "200": { "description": "Study registry traffic per endpoint and window", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuotarHealth" } } } } } } }, "/api/v0/main-frontend/credit-registration-admin/thresholds": { "get": { "tags": ["credit-registration-admin"], "summary": "\nGET `/api/v0/main-frontend/credit-registration-admin/thresholds` - Every number the alert rules and\nthe stuck detectors use.\n\nThe same values `/overview` embeds in its health block. Separate so a tab explaining \"stuck after\n2 hours\" can say so without reading the whole overview aggregate.", "operationId": "getCreditRegistrationThresholds", "responses": { "200": { "description": "The thresholds every rule and detector shares", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditRegistrationAlertThresholds" } } } } } } }, "/api/v0/main-frontend/credit-registrations/courses/{course_id}/consent": { "get": { "tags": ["credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/credit-registrations/courses/{course_id}/consent` - The signed-in\naccount's credit registration consent for one course.", "operationId": "getMyCourseCreditRegistrationConsent", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The caller's consent for the course", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MyCourseCreditRegistrationConsent" } } } } } }, "put": { "tags": ["credit-registrations"], "summary": "\nPUT `/api/v0/main-frontend/credit-registrations/courses/{course_id}/consent` - Records the signed-in\naccount's answer and applies it to that course's registrations at once.", "operationId": "setMyCourseCreditRegistrationConsent", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetMyCourseCreditRegistrationConsentPayload" } } }, "required": true }, "responses": { "200": { "description": "The recorded answer and what it unblocked", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetMyCourseCreditRegistrationConsentResult" } } } } } } }, "/api/v0/main-frontend/credit-registrations/my": { "get": { "tags": ["credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/credit-registrations/my` - Every credit registration of the signed-in\naccount, newest completion first.", "operationId": "getMyCreditRegistrations", "responses": { "200": { "description": "The caller's credit registrations", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/MyCreditRegistration" } } } } } } } }, "/api/v0/main-frontend/credit-registrations/my/by-course-module/{course_module_id}": { "get": { "tags": ["credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/credit-registrations/my/by-course-module/{course_module_id}` - The\nsigned-in account's registration for one course module, or null when the pipeline has not created one\nyet.", "operationId": "getMyCreditRegistrationForCourseModule", "parameters": [ { "name": "course_module_id", "in": "path", "description": "Course module id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The caller's registration for the module", "content": { "application/json": { "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/MyCreditRegistrationForCourseModule" } ] } } } } } } }, "/api/v0/main-frontend/credit-registrations/my/consents": { "get": { "tags": ["credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/credit-registrations/my/consents` - One row per course the signed-in\naccount is enrolled on that offers credit registration, asked or not.", "operationId": "getMyCreditRegistrationConsents", "responses": { "200": { "description": "The caller's per-course consents", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/MyCreditRegistrationConsent" } } } } } } } }, "/api/v0/main-frontend/credit-registrations/my/enrolment-banners/by-course/{course_id}": { "get": { "tags": ["credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/credit-registrations/my/enrolment-banners/by-course/{course_id}` - The\ncaller's registrations on one course that owe them the in-course re-enrol banner.\n\nScoped to the course rather than filtered from `/my` on the client, because every course-material page\nview calls this. Empty is the normal answer.", "operationId": "getMyCreditRegistrationEnrolmentBanners", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The caller's undismissed enrolment banners on the course", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/MyCreditRegistration" } } } } } } } }, "/api/v0/main-frontend/credit-registrations/my/student-number": { "get": { "tags": ["credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/credit-registrations/my/student-number` - The student number linked to the\nsigned-in account, or null.", "operationId": "getMyVerifiedStudentNumber", "responses": { "200": { "description": "The caller's linked student number", "content": { "application/json": { "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/MyVerifiedStudentNumber" } ] } } } } } }, "delete": { "tags": ["credit-registrations"], "summary": "\nDELETE `/api/v0/main-frontend/credit-registrations/my/student-number` - Unlinks the student number\nfrom the signed-in account.\n\nRegistrations that have not been sent go back to waiting; credits already in Sisu are untouched.", "operationId": "unlinkMyStudentNumber", "responses": { "200": { "description": "How many registrations went back to waiting", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UnlinkMyStudentNumberResult" } } } } } } }, "/api/v0/main-frontend/credit-registrations/my/student-number/dismiss-auto-link-notice": { "post": { "tags": ["credit-registrations"], "summary": "\nPOST `/api/v0/main-frontend/credit-registrations/my/student-number/dismiss-auto-link-notice` - Puts\naway the notice saying the pipeline linked this student number without asking.\n\nDismissing only hides the notice; the number stays linked and the unlink endpoint stays available.", "operationId": "dismissMyAutoLinkNotice", "responses": { "200": { "description": "The notice is dismissed" } } } }, "/api/v0/main-frontend/credit-registrations/my/{id}/dismiss-enrolment-banner": { "post": { "tags": ["credit-registrations"], "summary": "\nPOST `/api/v0/main-frontend/credit-registrations/my/{id}/dismiss-enrolment-banner` - Puts away the\nin-course re-enrol banner for one registration.\n\nIdempotent. Not a permanent opt-out: a later entry into the same state clears the dismissal.", "operationId": "dismissCreditRegistrationEnrolmentBanner", "parameters": [ { "name": "id", "in": "path", "description": "Credit registration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "The banner is dismissed" }, "403": { "description": "Not the caller's registration" } } } }, "/api/v0/main-frontend/credit-registrations/my/{id}/recheck-enrolment": { "post": { "tags": ["credit-registrations"], "summary": "\nPOST `/api/v0/main-frontend/credit-registrations/my/{id}/recheck-enrolment` - Asks the pipeline to\nlook for an enrolment again, for a row parked because the study registry had none.", "operationId": "requestCreditRegistrationEnrolmentRecheck", "parameters": [ { "name": "id", "in": "path", "description": "Credit registration id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Whether a recheck was started", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestCreditRegistrationEnrolmentRecheckResult" } } } }, "400": { "description": "The registration is not waiting for an enrolment" }, "403": { "description": "Not the caller's registration" } } } }, "/api/v0/main-frontend/credit-registrations/student-number-verifications/{token}": { "get": { "tags": ["credit-registrations"], "summary": "\nGET `/api/v0/main-frontend/credit-registrations/student-number-verifications/{token}` - What the\nmailed link would link, without linking it.\n\nWrites nothing: the link has to survive a mail scanner fetching it.", "operationId": "previewStudentNumberVerificationToken", "parameters": [ { "name": "token", "in": "path", "description": "The mailed verification token", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "What the token would link", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StudentNumberVerificationTokenPreview" } } } }, "404": { "description": "No such token" } } } }, "/api/v0/main-frontend/credit-registrations/student-number-verifications/{token}/claim": { "post": { "tags": ["credit-registrations"], "summary": "\nPOST `/api/v0/main-frontend/credit-registrations/student-number-verifications/{token}/claim` - Spends\na mailed link and links the student number to the signed-in account.\n\nAny signed-in account may claim any valid token: holding it proves control of the Sisu-held mailbox,\nand the session says which of our accounts the person wants to use.", "operationId": "claimStudentNumberVerificationToken", "parameters": [ { "name": "token", "in": "path", "description": "The mailed verification token", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "What the claim did", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClaimStudentNumberVerificationTokenResult" } } } }, "404": { "description": "No such token" } } } }, "/api/v0/main-frontend/email-templates": { "get": { "tags": ["email_templates"], "summary": "\nGET `/api/v0/main-frontend/email-templates`", "operationId": "getEmailTemplates", "responses": { "200": { "description": "Email templates", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/EmailTemplate" } } } } } } } }, "/api/v0/main-frontend/email-templates/{email_template_id}": { "delete": { "tags": ["email_templates"], "summary": "\nDELETE `/api/v0/main-frontend/email-templates/:id`", "operationId": "deleteEmailTemplate", "parameters": [ { "name": "email_template_id", "in": "path", "description": "Email template id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted email template", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailTemplate" } } } } } } }, "/api/v0/main-frontend/email-verification/request": { "post": { "tags": ["email-verification"], "summary": "\nPOST `/api/v0/main-frontend/email-verification/request` - Mails a fresh verification code to the\nsigned-in account's current address.", "operationId": "requestEmailVerificationCode", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestEmailVerificationPayload" } } }, "required": true }, "responses": { "200": { "description": "What the request did", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RequestEmailVerificationOutcome" } } } }, "404": { "description": "Email ownership verification is switched off" } } } }, "/api/v0/main-frontend/email-verification/status": { "get": { "tags": ["email-verification"], "summary": "\nGET `/api/v0/main-frontend/email-verification/status` - Whether the signed-in account's address is\nproven, and what we last mailed about it.", "operationId": "getMyEmailVerificationStatus", "responses": { "200": { "description": "Email verification status of the signed-in user", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailVerificationStatus" } } } } } } }, "/api/v0/main-frontend/email-verification/test-mode-code": { "get": { "tags": ["email-verification"], "summary": "\nGET `/api/v0/main-frontend/email-verification/test-mode-code` - The signed-in account's own pending\nverification code.\n\nExists because the system tests have no mail capture. 404 unless `TEST_MODE` is on, and scoped to the\ncaller's own account, so an accidentally open gate only ever hands you your own code.", "operationId": "getEmailVerificationCodeForTestMode", "responses": { "200": { "description": "The caller's pending verification code", "content": { "text/plain": { "schema": { "type": "string" } } } }, "404": { "description": "Not in test mode, or no pending code" } } } }, "/api/v0/main-frontend/email-verification/verify": { "post": { "tags": ["email-verification"], "summary": "\nPOST `/api/v0/main-frontend/email-verification/verify` - Spends a mailed code and records the proof.\n\nAuthenticated and scoped to the caller's own account, so the code is the only secret involved and it\nnever has to identify anybody on its own.", "operationId": "verifyEmailOwnership", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VerifyEmailOwnershipPayload" } } }, "required": true }, "responses": { "200": { "description": "Outcome of submitting the code", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VerifyEmailOwnershipResult" } } } }, "404": { "description": "Email ownership verification is switched off" } } } }, "/api/v0/main-frontend/exams/{exam_id}/exam-exercises": { "get": { "tags": ["exams"], "summary": "\nGET `/api/v0/main-frontend/exam/:exam_id/exam-exercises` - Returns all the exercises with exam_id.", "operationId": "getExamExercises", "parameters": [ { "name": "exam_id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exam exercises", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Exercise" } } } } } } } }, "/api/v0/main-frontend/exams/{exam_id}/release-grades": { "post": { "tags": ["exams"], "summary": "\nPOST `/api/v0/main-frontend/exam/:exam_id/release-grades` - Publishes grading results of an exam by updating user_exercise_states according to teacher_grading_decisons and changes teacher_grading_decisions hidden field to false. Takes teacher grading decision ids as input.", "operationId": "releaseExamGrades", "parameters": [ { "name": "exam_id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "required": true }, "responses": { "200": { "description": "Exam grades released" } } } }, "/api/v0/main-frontend/exams/{exam_id}/submissions-with-exam-id": { "get": { "tags": ["exams"], "summary": "\nGET `/api/v0/main-frontend/exam/:exam_id/submissions-with-exam-id` - Returns all the exercise submissions and user exercise states with exam_id.", "operationId": "getExamSubmissionsWithExamId", "parameters": [ { "name": "exam_id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Exercise submissions with exam id", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmissionAndUserExerciseState" } } } } } } } } }, "/api/v0/main-frontend/exams/{exercise_id}/submissions-with-exercise-id": { "get": { "tags": ["exams"], "summary": "\nGET `/api/v0/main-frontend/exam/:exercise_id/submissions-with-exercise_id` - Returns all the exercise submissions and user exercise states with exercise_id.", "operationId": "getExamSubmissionsWithExerciseId", "parameters": [ { "name": "exercise_id", "in": "path", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int32", "minimum": 0 } } ], "responses": { "200": { "description": "Exercise submissions with exercise id", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseSlideSubmissionAndUserExerciseStateList" } } } } } } }, "/api/v0/main-frontend/exams/{id}": { "get": { "tags": ["exams"], "summary": "\nGET `/api/v0/main-frontend/exams/:id", "operationId": "getExam", "parameters": [ { "name": "id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exam", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Exam" } } } } } } }, "/api/v0/main-frontend/exams/{id}/duplicate": { "post": { "tags": ["exams"], "summary": "\n * POST `/api/v0/cms/exams/:exam_id/duplicate` - duplicates existing exam.", "operationId": "duplicateExam", "parameters": [ { "name": "id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewExam" } } }, "required": true }, "responses": { "200": { "description": "Exam duplicated", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/exams/{id}/edit-exam": { "post": { "tags": ["exams"], "summary": "\nPOST `/api/v0/main-frontend/organizations/{organization_id}/edit-exam` - edits an exam.", "operationId": "editExam", "parameters": [ { "name": "id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewExam" } } }, "required": true }, "responses": { "200": { "description": "Exam edited" } } } }, "/api/v0/main-frontend/exams/{id}/export-points": { "get": { "tags": ["exams"], "summary": "\nGET `/api/v0/main-frontend/exams/:id/export-points`", "operationId": "exportExamPointsCsv", "parameters": [ { "name": "id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exam points CSV export", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/exams/{id}/export-submissions": { "get": { "tags": ["exams"], "summary": "\nGET `/api/v0/main-frontend/exams/:id/export-submissions`", "operationId": "exportExamSubmissionsCsv", "parameters": [ { "name": "id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exam submissions CSV export", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/exams/{id}/set": { "post": { "tags": ["exams"], "summary": "\nPOST `/api/v0/main-frontend/exams/:id/set`", "operationId": "setExamCourse", "parameters": [ { "name": "id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExamCourseInfo" } } }, "required": true }, "responses": { "200": { "description": "Course set for exam" } } } }, "/api/v0/main-frontend/exams/{id}/unset": { "post": { "tags": ["exams"], "summary": "\nPOST `/api/v0/main-frontend/exams/:id/unset`", "operationId": "unsetExamCourse", "parameters": [ { "name": "id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExamCourseInfo" } } }, "required": true }, "responses": { "200": { "description": "Course unset from exam" } } } }, "/api/v0/main-frontend/exercise-repositories/course/{course_id}": { "get": { "tags": ["exercise_repositories"], "summary": "\nGET `/api/v0/main-frontend/exercise-repositories/course/:id`", "operationId": "getExerciseRepositoriesForCourse", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise repositories for course", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseRepository" } } } } } } } }, "/api/v0/main-frontend/exercise-repositories/exam/{exam_id}": { "get": { "tags": ["exercise_repositories"], "summary": "\nGET `/api/v0/main-frontend/exercise-repositories/exam/:id`", "operationId": "getExerciseRepositoriesForExam", "parameters": [ { "name": "exam_id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise repositories for exam", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseRepository" } } } } } } } }, "/api/v0/main-frontend/exercise-repositories/new": { "post": { "tags": ["exercise_repositories"], "summary": "\nPOST `/api/v0/main-frontend/exercise-repositories/new", "operationId": "createExerciseRepository", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewExerciseRepository" } } }, "required": true }, "responses": { "200": { "description": "Created exercise repository id", "content": { "text/plain": { "schema": { "type": "string", "format": "uuid" } } } } } } }, "/api/v0/main-frontend/exercise-repositories/{id}": { "put": { "tags": ["exercise_repositories"], "summary": "\nPUT `/api/v0/main-frontend/exercise-repositories/:id`", "operationId": "updateExerciseRepository", "parameters": [ { "name": "id", "in": "path", "description": "Exercise repository id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseRepositoryUpdate" } } }, "required": true }, "responses": { "200": { "description": "Updated exercise repository", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } }, "delete": { "tags": ["exercise_repositories"], "summary": "\nDELETE `/api/v0/main-frontend/exercise-repositories/:id`", "operationId": "deleteExerciseRepository", "parameters": [ { "name": "id", "in": "path", "description": "Exercise repository id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted exercise repository", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/exercise-services/": { "get": { "tags": ["exercise_services"], "summary": "\nGET `/api/v0/main-frontend/exercise-services`", "operationId": "getExerciseServices", "responses": { "200": { "description": "Exercise services", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseService" } } } } } } }, "post": { "tags": ["exercise_services"], "summary": "\nPOST `/api/v0/main-frontend/exercise-services`", "operationId": "createExerciseService", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseServiceNewOrUpdate" } } }, "required": true }, "responses": { "200": { "description": "Created exercise service", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseServiceWithError" } } } } } } }, "/api/v0/main-frontend/exercise-services/{exercise_service_id}": { "get": { "tags": ["exercise_services"], "summary": "\nGET `/api/v0/main-frontend/exercise-services/:id`", "operationId": "getExerciseServiceById", "parameters": [ { "name": "exercise_service_id", "in": "path", "description": "Exercise service id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise service", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseService" } } } } } }, "put": { "tags": ["exercise_services"], "summary": "\nPUT `/api/v0/main-frontend/exercise-services/:id`", "operationId": "updateExerciseService", "parameters": [ { "name": "exercise_service_id", "in": "path", "description": "Exercise service id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseServiceNewOrUpdate" } } }, "required": true }, "responses": { "200": { "description": "Updated exercise service", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseServiceWithError" } } } } } }, "delete": { "tags": ["exercise_services"], "operationId": "deleteExerciseService", "parameters": [ { "name": "exercise_service_id", "in": "path", "description": "Exercise service id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted exercise service", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseService" } } } } } } }, "/api/v0/main-frontend/exercise-slide-submissions/add-teacher-grading-for-exam-submission": { "put": { "tags": ["exercise_slide_submissions"], "summary": "\nPUT `/api/v0/main-frontend/exercise-slide-submissions/add_teacher_grading\"` - Adds a new teacher grading decision, without updating user exercise state", "operationId": "addTeacherGradingForExamSubmission", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewTeacherGradingDecision" } } }, "required": true }, "responses": { "200": { "description": "Created teacher grading decision", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TeacherGradingDecision" } } } } } } }, "/api/v0/main-frontend/exercise-slide-submissions/{exam_id}/user-exercise-state-info": { "get": { "tags": ["exercise_slide_submissions"], "summary": "\nGET `/api/v0/main-frontend/exercise-slide-submissions/{exam_id}/{exercise_id}/{user_id}/user-exercise-state-info`-", "operationId": "getExamUserExerciseStateInfo", "parameters": [ { "name": "exam_id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "exercise_id", "in": "query", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "query", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User exercise state for the exam submission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserExerciseState" } } } } } } }, "/api/v0/main-frontend/exercise-slide-submissions/{submission_id}/info": { "get": { "tags": ["exercise_slide_submissions"], "summary": "\nGET `/api/v0/main-frontend/exercise-slide-submissions/{submission_id}/info\"`- Returns data necessary for rendering a submission.", "operationId": "getExerciseSlideSubmissionInfo", "parameters": [ { "name": "submission_id", "in": "path", "description": "Exercise slide submission id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise slide submission info", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseSlideSubmissionInfo" } } } } } } }, "/api/v0/main-frontend/exercises/{course_id}/exercises-by-course-id": { "get": { "tags": ["exercises"], "summary": "\nGET `/api/v0/main-frontend/exercises/:course_id/exercises-by-course-id` - Returns all exercises for a course with course_id", "operationId": "getExercisesByCourseId", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercises by course id", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Exercise" } } } } } } } }, "/api/v0/main-frontend/exercises/{course_id}/reset-exercises-for-selected-users": { "post": { "tags": ["exercises"], "summary": "\nPOST `/api/v0/main-frontend/exercises/:course_id/reset-exercises-for-selected-users` - Resets all selected exercises for selected users and then logs the resets to exercise_reset_logs table", "operationId": "resetExercisesForSelectedUsers", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResetExercisesPayload" } } }, "required": true }, "responses": { "200": { "description": "Number of successful resets", "content": { "text/plain": { "schema": { "type": "integer", "format": "int32" } } } } } } }, "/api/v0/main-frontend/exercises/{exercise_id}": { "get": { "tags": ["exercises"], "summary": "\nGET `/api/v0/main-frontend/exercises/:exercise_id` - Returns a single exercise.", "operationId": "getExercise", "parameters": [ { "name": "exercise_id", "in": "path", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Exercise" } } } } } } }, "/api/v0/main-frontend/exercises/{exercise_id}/answers-requiring-attention": { "get": { "tags": ["exercises"], "summary": "\nGET `/api/v0/main-frontend/exercises/:exercise_id/answers-requiring-attention` - Returns an exercise's answers requiring attention.", "operationId": "getExerciseAnswersRequiringAttention", "parameters": [ { "name": "exercise_id", "in": "path", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Answers requiring attention", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnswersRequiringAttention" } } } } } } }, "/api/v0/main-frontend/exercises/{exercise_id}/csv-export-task-options": { "get": { "tags": ["exercises"], "summary": "\nGET `/api/v0/main-frontend/exercises/:exercise_id/csv-export-task-options` - Returns available exercise tasks and CSV export support flags for each task's exercise service.", "operationId": "getExerciseCsvExportTaskOptions", "parameters": [ { "name": "exercise_id", "in": "path", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise CSV export task options", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseCsvExportTaskOption" } } } } } } } }, "/api/v0/main-frontend/exercises/{exercise_id}/export-answers-csv": { "get": { "tags": ["exercises"], "summary": "\nGET `/api/v0/main-frontend/exercises/:exercise_id/export-answers-csv` - Exports all answers for one exercise task as CSV using the task's exercise service.", "operationId": "exportExerciseAnswersCsv", "parameters": [ { "name": "exercise_id", "in": "path", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "exercise_task_id", "in": "query", "description": "Exercise task id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "only_latest_per_user", "in": "query", "description": "Only include latest submission per user", "required": false, "schema": { "type": "boolean" } } ], "responses": { "200": { "description": "Exercise answers CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/exercises/{exercise_id}/export-definitions-csv": { "get": { "tags": ["exercises"], "summary": "\nGET `/api/v0/main-frontend/exercises/:exercise_id/export-definitions-csv` - Exports one exercise task definition as CSV using the task's exercise service.", "operationId": "exportExerciseDefinitionsCsv", "parameters": [ { "name": "exercise_id", "in": "path", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "exercise_task_id", "in": "query", "description": "Exercise task id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "only_latest_per_user", "in": "query", "description": "Only include latest submission per user", "required": false, "schema": { "type": "boolean" } } ], "responses": { "200": { "description": "Exercise definitions CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/exercises/{exercise_id}/submissions": { "get": { "tags": ["exercises"], "summary": "\nGET `/api/v0/main-frontend/exercises/:exercise_id/submissions` - Returns an exercise's submissions.", "operationId": "getExerciseSubmissions", "parameters": [ { "name": "exercise_id", "in": "path", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Exercise submissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseSubmissions" } } } } } } }, "/api/v0/main-frontend/exercises/{exercise_id}/submissions/user/{user_id}": { "get": { "tags": ["exercises"], "summary": "\nGET `/api/v0/main-frontend/exercises/:exercise_id/submissions/user/:user_id` - Returns an exercise's submissions for a user.", "operationId": "getExerciseSubmissionsForUser", "parameters": [ { "name": "exercise_id", "in": "path", "description": "Exercise id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Exercise submissions for user", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmission" } } } } } } } }, "/api/v0/main-frontend/feedback/{feedback_id}": { "post": { "tags": ["feedback"], "summary": "\nPOST `/api/v0/main-frontend/feedback/:id` - Creates new feedback.", "operationId": "markFeedbackAsRead", "parameters": [ { "name": "feedback_id", "in": "path", "description": "Feedback id", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MarkAsRead" } } }, "required": true }, "responses": { "200": { "description": "Feedback read state updated" } } } }, "/api/v0/main-frontend/global-stats/completion-stats-by-email-domain": { "get": { "tags": ["global-stats"], "summary": "\n * GET `/api/v0/main-frontend/global-stats/completion-stats-by-email-domain`\n *\n * Query parameters:\n * - year: Optional - Filter results to specific year (e.g. ?year=2023)", "operationId": "getCompletionStatsByEmailDomain", "parameters": [ { "name": "year", "in": "query", "description": "Optional year", "required": false, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Completion stats by email domain", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/DomainCompletionStats" } } } } } } } }, "/api/v0/main-frontend/global-stats/course-completion-stats-for-email-domain": { "get": { "tags": ["global-stats"], "summary": "\n * GET `/api/v0/main-frontend/global-stats/course-completion-stats-for-email-domain`\n *\n * Query parameters:\n * - email_domain: String - The email domain to get stats for (required)\n * - year: Optional - Filter results to specific year (e.g. ?year=2023)", "operationId": "getCourseCompletionStatsForEmailDomain", "parameters": [ { "name": "email_domain", "in": "query", "description": "Email domain", "required": true, "schema": { "type": "string" } }, { "name": "year", "in": "query", "description": "Optional year", "required": false, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Course completion stats for email domain", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseCompletionStats" } } } } } } } }, "/api/v0/main-frontend/global-stats/course-module-stats-by-completions-registered-to-study-registry": { "get": { "tags": ["global-stats"], "summary": "\n * GET `/api/v0/main-frontend/global-stats/course-module-stats-by-completions-registered-to-study-registry`\n *\n * Query parameters:\n * - granularity: String - Either \"year\" or \"month\" (defaults to \"year\")", "operationId": "getCourseModuleStatsByCompletionsRegisteredToStudyRegistry", "parameters": [ { "name": "granularity", "in": "query", "description": "Time granularity", "required": false, "schema": { "$ref": "#/components/schemas/TimeGranularity" } } ], "responses": { "200": { "description": "Course module completion stats", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/GlobalCourseModuleStatEntry" } } } } } } } }, "/api/v0/main-frontend/global-stats/number-of-people-completed-a-course": { "get": { "tags": ["global-stats"], "summary": "\nGET `/api/v0/main-frontend/global-stats/number-of-people-completed-a-course`\n\nQuery parameters:\n- granularity: String - Either \"year\" or \"month\" (defaults to \"year\")", "operationId": "getNumberOfPeopleCompletedACourse", "parameters": [ { "name": "granularity", "in": "query", "description": "Time granularity", "required": false, "schema": { "$ref": "#/components/schemas/TimeGranularity" } } ], "responses": { "200": { "description": "Global completion stats", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/GlobalStatEntry" } } } } } } } }, "/api/v0/main-frontend/global-stats/number-of-people-done-at-least-one-exercise": { "get": { "tags": ["global-stats"], "summary": "\nGET `/api/v0/main-frontend/global-stats/number-of-people-done-at-least-one-exercise`\n\nQuery parameters:\n- granularity: String - Either \"year\" or \"month\" (defaults to \"year\")", "operationId": "getNumberOfPeopleDoneAtLeastOneExercise", "parameters": [ { "name": "granularity", "in": "query", "description": "Time granularity", "required": false, "schema": { "$ref": "#/components/schemas/TimeGranularity" } } ], "responses": { "200": { "description": "Exercise participation stats", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/GlobalStatEntry" } } } } } } } }, "/api/v0/main-frontend/global-stats/number-of-people-registered-completion-to-study-registry": { "get": { "tags": ["global-stats"], "summary": "\nGET `/api/v0/main-frontend/global-stats/number-of-people-registered-completion-to-study-registry`\n\nQuery parameters:\n- granularity: String - Either \"year\" or \"month\" (defaults to \"year\")", "operationId": "getNumberOfPeopleRegisteredCompletionToStudyRegistry", "parameters": [ { "name": "granularity", "in": "query", "description": "Time granularity", "required": false, "schema": { "$ref": "#/components/schemas/TimeGranularity" } } ], "responses": { "200": { "description": "Study registry completion stats", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/GlobalStatEntry" } } } } } } } }, "/api/v0/main-frontend/global-stats/number-of-people-started-course": { "get": { "tags": ["global-stats"], "summary": "\nGET `/api/v0/main-frontend/global-stats/number-of-people-started-course`\n\nQuery parameters:\n- granularity: String - Either \"year\" or \"month\" (defaults to \"year\")", "operationId": "getNumberOfPeopleStartedCourse", "parameters": [ { "name": "granularity", "in": "query", "description": "Time granularity", "required": false, "schema": { "$ref": "#/components/schemas/TimeGranularity" } } ], "responses": { "200": { "description": "Course start stats", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/GlobalStatEntry" } } } } } } } }, "/api/v0/main-frontend/glossary/{term_id}": { "put": { "tags": ["glossary"], "operationId": "updateGlossaryTerm", "parameters": [ { "name": "term_id", "in": "path", "description": "Glossary term id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TermUpdate" } } }, "required": true }, "responses": { "200": { "description": "Glossary term updated" }, "401": { "description": "Authentication required" }, "403": { "description": "User is not allowed to manage glossary terms" } } }, "delete": { "tags": ["glossary"], "operationId": "deleteGlossaryTerm", "parameters": [ { "name": "term_id", "in": "path", "description": "Glossary term id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Glossary term deleted" }, "401": { "description": "Authentication required" }, "403": { "description": "User is not allowed to manage glossary terms" } } } }, "/api/v0/main-frontend/oauth/.well-known/openid-configuration": { "get": { "tags": ["oauth"], "summary": "Handles `/.well-known/openid-configuration` to expose OIDC discovery metadata.", "description": "This endpoint advertises the AS/OP capabilities so clients can auto-configure:\n- Endpoints (authorize, token, userinfo, jwks)\n- Supported response/grant types\n- Token endpoint auth methods\n- ID Token signing algs\n- PKCE and DPoP metadata\n\nFollows:\n- [OIDC Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html)\n- [RFC 8414 — OAuth 2.0 Authorization Server Metadata](https://www.rfc-editor.org/rfc/rfc8414)\n- [RFC 9449 — DPoP metadata](https://www.rfc-editor.org/rfc/rfc9449#name-authorization-server-metadata)\n\n# Example\n```http\nGET /api/v0/main-frontend/oauth/.well-known/openid-configuration HTTP/1.1\n```\n\nExample response (truncated):\n```json\n{\n \"issuer\": \"https://example.org/api/v0/main-frontend/oauth\",\n \"authorization_endpoint\": \"https://example.org/api/v0/main-frontend/oauth/authorize\",\n \"token_endpoint\": \"https://example.org/api/v0/main-frontend/oauth/token\",\n \"userinfo_endpoint\": \"https://example.org/api/v0/main-frontend/oauth/userinfo\",\n \"jwks_uri\": \"https://example.org/api/v0/main-frontend/oauth/jwks.json\",\n \"response_types_supported\": [\"code\"],\n \"grant_types_supported\": [\"authorization_code\",\"refresh_token\"],\n \"code_challenge_methods_supported\": [\"S256\"],\n \"token_endpoint_auth_methods_supported\": [\"none\",\"client_secret_post\"],\n \"id_token_signing_alg_values_supported\": [\"RS256\"],\n \"subject_types_supported\": [\"public\"],\n \"dpop_signing_alg_values_supported\": [\"ES256\",\"RS256\"]\n}\n```", "operationId": "getOauthOpenidConfiguration", "responses": { "200": { "description": "OAuth/OpenID Connect discovery document", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/oauth/authorize": { "get": { "tags": ["oauth"], "operationId": "authorizeOauthGet", "parameters": [ { "name": "response_type", "in": "query", "description": "OAuth response type", "required": false, "schema": { "type": "string" } }, { "name": "client_id", "in": "query", "description": "OAuth client id", "required": false, "schema": { "type": "string" } }, { "name": "redirect_uri", "in": "query", "description": "Redirect URI", "required": false, "schema": { "type": "string" } }, { "name": "scope", "in": "query", "description": "Requested scopes", "required": false, "schema": { "type": "string" } }, { "name": "state", "in": "query", "description": "OAuth state", "required": false, "schema": { "type": "string" } }, { "name": "nonce", "in": "query", "description": "OpenID Connect nonce", "required": false, "schema": { "type": "string" } }, { "name": "code_challenge", "in": "query", "description": "PKCE code challenge", "required": false, "schema": { "type": "string" } }, { "name": "code_challenge_method", "in": "query", "description": "PKCE code challenge method", "required": false, "schema": { "type": "string" } }, { "name": "prompt", "in": "query", "description": "Prompt behavior", "required": false, "schema": { "type": "string" } }, { "name": "request", "in": "query", "description": "Unsupported request object", "required": false, "schema": { "type": "string" } } ], "responses": { "302": { "description": "Redirect to login, consent, or client redirect URI" } } }, "post": { "tags": ["oauth"], "operationId": "authorizeOauthPost", "requestBody": { "content": { "application/x-www-form-urlencoded": { "schema": {} } }, "required": true }, "responses": { "302": { "description": "Redirect to login, consent, or client redirect URI" } } } }, "/api/v0/main-frontend/oauth/authorized-clients": { "get": { "tags": ["oauth"], "operationId": "getOauthAuthorizedClients", "responses": { "200": { "description": "Authorized OAuth clients", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/AuthorizedClientInfo" } } } } } } } }, "/api/v0/main-frontend/oauth/authorized-clients/{client_id}": { "delete": { "tags": ["oauth"], "operationId": "deleteOauthAuthorizedClient", "parameters": [ { "name": "client_id", "in": "path", "description": "OAuth client id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "204": { "description": "Authorized client revoked" } } } }, "/api/v0/main-frontend/oauth/consent": { "post": { "tags": ["oauth"], "summary": "Handles `/consent` approval after the user agrees to grant requested scopes.", "description": "This endpoint:\n- Validates the redirect URI and requested scopes against the registered client.\n- Records granted scopes for the user-client pair.\n- Redirects back to `/authorize` to continue the OAuth flow.\n\n# Example\n```http\nGET /api/v0/main-frontend/oauth/consent?client_id=test-client-id&redirect_uri=http://localhost&scopes=openid%20profile&state=random123&nonce=secure_nonce_abc HTTP/1.1\nCookie: session=abc123\n\n```\n\nRedirect back to `/authorize`:\n```http\nHTTP/1.1 302 Found\nLocation: /api/v0/main-frontend/oauth/authorize?client_id=...\n```", "operationId": "approveOauthConsent", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConsentQuery" } } }, "required": true }, "responses": { "200": { "description": "Consent approval response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConsentResponse" } } } } } } }, "/api/v0/main-frontend/oauth/consent/deny": { "post": { "tags": ["oauth"], "summary": "Handles `/consent/deny` when the user refuses to grant scopes.", "description": "This endpoint:\n- Redirects back to the client with `error=access_denied`.\n\n# Example\n```http\nGET /api/v0/main-frontend/oauth/consent/deny?redirect_uri=http://localhost&state=random123 HTTP/1.1\n\n```\n\nResponse:\n```http\nHTTP/1.1 302 Found\nLocation: http://localhost?error=access_denied&state=random123\n```", "operationId": "denyOauthConsent", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConsentDenyQuery" } } }, "required": true }, "responses": { "200": { "description": "Consent denial response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConsentResponse" } } } } } } }, "/api/v0/main-frontend/oauth/device_authorization": { "post": { "tags": ["oauth"], "summary": "Handles `POST /device_authorization` — the RFC 8628 device authorization\nendpoint.", "description": "Public, form-encoded, no session required (rate-limited like `/token`).\nValidates the client, checks it is allowed the device-code grant, validates\nrequested scopes against the client's registered scopes, then issues an\nopaque `device_code` (stored only as an HMAC digest) plus a human-typable\n`user_code`.\n\nFollows [RFC 8628 §3.1–§3.2](https://datatracker.ietf.org/doc/html/rfc8628#section-3.1).\n\n# Example\n```http\nPOST /api/v0/main-frontend/oauth/device_authorization HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\nclient_id=tmc-vscode&scope=exercise-services\n```\n\nSuccessful response:\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n \"device_code\": \"GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS\",\n \"user_code\": \"WDJB-MJHT\",\n \"verification_uri\": \"https://courses.mooc.fi/oauth_device\",\n \"verification_uri_complete\": \"https://courses.mooc.fi/oauth_device?user_code=WDJB-MJHT\",\n \"expires_in\": 900,\n \"interval\": 5\n}\n```", "operationId": "deviceAuthorizationOauth", "requestBody": { "content": { "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/DeviceAuthorizationForm" } } }, "required": true }, "responses": { "200": { "description": "Device authorization response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceAuthorizationResponse" } } } }, "400": { "description": "OAuth error (invalid_scope, unauthorized_client)" }, "401": { "description": "OAuth error (invalid_client)" } } } }, "/api/v0/main-frontend/oauth/device_verification": { "get": { "tags": ["oauth"], "summary": "Handles `GET /device_verification` — render data for the browser consent page.", "description": "Session-authed. Looks up the still-pending, unexpired grant for the given\n`user_code` (normalizing the input first) and returns the client name and\nrequested scopes so the page can ask the user to approve. A code that is\nunknown, expired, or no longer pending yields `404 Not Found` so the page can\nshow a distinguishable \"invalid or expired code\" message.", "operationId": "getOauthDeviceVerification", "parameters": [ { "name": "user_code", "in": "query", "description": "The user_code shown to the user by the device", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Pending device authorization render data", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceVerificationInfo" } } } }, "404": { "description": "No pending device authorization for this user_code" } } } }, "/api/v0/main-frontend/oauth/device_verification/approve": { "post": { "tags": ["oauth"], "summary": "Handles `POST /device_verification/approve`.", "description": "Session-authed. Persists the user's consent via the same\n`OAuthUserClientScopes::insert` the web consent flow uses (consent is always\nrecorded — never short-circuited on a pre-existing grant), then marks the\ndevice code approved and bound to the signed-in user.", "operationId": "approveOauthDeviceVerification", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceDecisionBody" } } }, "required": true }, "responses": { "200": { "description": "Device authorization approved", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceDecisionResponse" } } } }, "404": { "description": "No pending device authorization for this user_code" } } } }, "/api/v0/main-frontend/oauth/device_verification/deny": { "post": { "tags": ["oauth"], "summary": "Handles `POST /device_verification/deny`.", "description": "Session-authed. Marks the pending device code denied; the polling client then\nreceives `access_denied` at the token endpoint.", "operationId": "denyOauthDeviceVerification", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceDecisionBody" } } }, "required": true }, "responses": { "200": { "description": "Device authorization denied", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeviceDecisionResponse" } } } }, "404": { "description": "No pending device authorization for this user_code" } } } }, "/api/v0/main-frontend/oauth/introspect": { "post": { "tags": ["oauth"], "summary": "Handles the `/introspect` endpoint for OAuth 2.0 token introspection (RFC 7662).", "description": "This endpoint allows resource servers to query the authorization server about\nthe active state and metadata of an access token.\n\n### Security Features\n- Only a confidential client may introspect; an unknown client, a public client or a bad\n secret is 401 `invalid_client` (RFC 7662 §2.1, §2.3)\n- Returns 200 with `active: false` for an invalid/expired *token* (RFC 7662 §2.1), so token\n existence is never disclosed to an authenticated caller\n\n### Request Parameters\n- `token` (required): The token to be introspected\n- `token_type_hint` (optional): Hint about token type (\"access_token\" or \"refresh_token\")\n- `client_id` (required): Client identifier of a confidential client\n- `client_secret` (required): Client secret\n\n### Response\nReturns a JSON object with:\n- `active` (bool, required): Whether the token is active\n- Additional fields only present if `active: true`:\n - `scope`: Space-separated list of scopes\n - `client_id`: Client identifier\n - `username`/`sub`: User identifier (if token has user)\n - `exp`: Expiration timestamp (Unix time)\n - `iat`: Issued at timestamp (Unix time)\n - `aud`: Audience\n - `iss`: Issuer\n - `jti`: JWT ID\n - `token_type`: \"Bearer\" or \"DPoP\"\n- Non-standard members, returned only to callers that authenticated as a\n confidential client and omitted (never falsified) otherwise:\n - `upstream_id`: the token owner's legacy TMC user id\n - `client_bearer_allowed`: whether the client the token was issued to may use it\n as a plain Bearer credential. Consumers must fail closed if it is absent.\n\nFollows [RFC 7662 — OAuth 2.0 Token Introspection](https://datatracker.ietf.org/doc/html/rfc7662).\n\n# Example\n```http\nPOST /api/v0/main-frontend/oauth/introspect HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\ntoken=ACCESS_TOKEN&client_id=test-client-id&client_secret=test-secret\n```\n\nSuccessful response:\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json\nCache-Control: no-store\n\n{\n \"active\": true,\n \"scope\": \"openid profile email\",\n \"client_id\": \"test-client-id\",\n \"sub\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"username\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"exp\": 1735689600,\n \"iat\": 1735686000,\n \"iss\": \"https://example.com/api/v0/main-frontend/oauth\",\n \"jti\": \"123e4567-e89b-12d3-a456-426614174000\",\n \"token_type\": \"Bearer\"\n}\n```\n\nInactive token response:\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json\nCache-Control: no-store\n\n{\n \"active\": false\n}\n```", "operationId": "introspectOauthToken", "requestBody": { "content": { "application/x-www-form-urlencoded": { "schema": {} } }, "required": true }, "responses": { "200": { "description": "OAuth token introspection response", "content": { "application/json": { "schema": {} } } }, "401": { "description": "Client authentication failed (invalid_client)" } } } }, "/api/v0/main-frontend/oauth/jwks.json": { "get": { "tags": ["oauth"], "summary": "Handles `/jwks.json` for returning the JSON Web Key Set (JWKS).", "description": "This endpoint:\n- Reads the configured ID Token signing public key (RS256).\n- Exposes it in JWKS format for clients to validate ID tokens.\n\nFollows [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517).\n\nNote: Currently exposes a single signing key. Key rotation (OIDC Core §10) is not implemented.\n\n# Example\n```http\nGET /api/v0/main-frontend/oauth/jwks.json HTTP/1.1\n```\n\nResponse:\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n \"keys\": [\n { \"kty\":\"RSA\",\"use\":\"sig\",\"alg\":\"RS256\",\"kid\":\"abc123\",\"n\":\"...\",\"e\":\"AQAB\" }\n ]\n}\n```", "operationId": "getOauthJwks", "responses": { "200": { "description": "OAuth JSON Web Key Set", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/oauth/revoke": { "post": { "tags": ["oauth"], "summary": "Handles the `/revoke` endpoint for OAuth 2.0 token revocation (RFC 7009).", "description": "This endpoint allows clients to revoke access tokens or refresh tokens.\n\n### Security Features\n- Client authentication is required (client_id and client_secret)\n- Always returns `200 OK` even for invalid/expired/already-revoked tokens\n to prevent token enumeration attacks\n- Validates that the token belongs to the authenticated client before revoking\n\n### Request Parameters\n- `token` (required): The token to be revoked\n- `token_type_hint` (optional): Hint about token type (\"access_token\" or \"refresh_token\")\n\nFollows [RFC 7009 — OAuth 2.0 Token Revocation](https://datatracker.ietf.org/doc/html/rfc7009).\n\n# Example\n```http\nPOST /api/v0/main-frontend/oauth/revoke HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\ntoken=ACCESS_TOKEN_TO_REVOKE&token_type_hint=access_token&client_id=test-client-id&client_secret=test-secret\n```\n\nResponse (always 200 OK):\n```http\nHTTP/1.1 200 OK\n```", "operationId": "revokeOauthToken", "requestBody": { "content": { "application/x-www-form-urlencoded": { "schema": {} } }, "required": true }, "responses": { "200": { "description": "OAuth token revocation acknowledged" } } } }, "/api/v0/main-frontend/oauth/token": { "post": { "tags": ["oauth"], "summary": "Handles the `/token` endpoint for exchanging authorization codes or refresh tokens.", "description": "This endpoint issues and rotates OAuth 2.0 and OpenID Connect tokens with support for\n**PKCE**, **DPoP sender-constrained tokens**, and **ID Token issuance**.\n\n### Authorization Code Grant\n- Validates client credentials (`client_id`, `client_secret`) or public client rules.\n- Verifies the authorization code, its redirect URI, PKCE binding (`code_verifier`), and expiration.\n- Optionally verifies a DPoP proof and binds the issued tokens to the DPoP JWK thumbprint (`dpop_jkt`).\n- Issues a new access token, refresh token, and (for OIDC requests) an ID token.\n\n### Refresh Token Grant\n- Validates the refresh token and client binding.\n- Verifies DPoP proof when applicable (must match the original `dpop_jkt`).\n- Rotates the refresh token (revokes the old one, inserts a new one linked to it).\n- Issues a new access token (and ID token if `openid` scope requested).\n\n### Security Features\n- **PKCE (RFC 7636)**: Enforced for public clients and optionally for confidential ones.\n- **DPoP (RFC 9449)**: Sender-constrains tokens to a JWK thumbprint.\n- **Refresh Token Rotation**: Prevents replay by revoking old RTs on use.\n- **OIDC ID Token**: Issued only if `openid` is in the granted scopes.\n\nFollows:\n- [RFC 6749 §3.2 — Token Endpoint](https://datatracker.ietf.org/doc/html/rfc6749#section-3.2)\n- [RFC 7636 — PKCE](https://datatracker.ietf.org/doc/html/rfc7636)\n- [RFC 9449 — DPoP](https://datatracker.ietf.org/doc/html/rfc9449)\n- [OIDC Core §3.1.3 — Token Endpoint](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint)\n\n# Example\n```http\nPOST /api/v0/main-frontend/oauth/token HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=http://localhost&client_id=test-client-id&client_secret=test-secret&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\n```\n\nSuccessful response:\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{\n \"access_token\": \"2YotnFZFEjr1zCsicMWpAA\",\n \"refresh_token\": \"tGzv3JOkF0XG5Qx2TlKWIA\",\n \"id_token\": \"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"token_type\": \"DPoP\",\n \"expires_in\": 3600\n}\n```\n\nExample error:\n```http\nHTTP/1.1 401 Unauthorized\nContent-Type: application/json\n\n{\n \"error\": \"invalid_client\",\n \"error_description\": \"invalid client secret\"\n}\n```\n\nExample DPoP error:\n```http\nHTTP/1.1 401 Unauthorized\nWWW-Authenticate: DPoP error=\"use_dpop_proof\", error_description=\"Missing DPoP header\"\n```", "operationId": "exchangeOauthToken", "requestBody": { "content": { "application/x-www-form-urlencoded": { "schema": {} } }, "required": true }, "responses": { "200": { "description": "OAuth token response", "content": { "application/json": { "schema": {} } } }, "401": { "description": "OAuth token error" } } } }, "/api/v0/main-frontend/oauth/userinfo": { "get": { "tags": ["oauth"], "operationId": "getOauthUserInfo", "responses": { "200": { "description": "OAuth userinfo response", "content": { "application/json": { "schema": {} } } }, "401": { "description": "Invalid token" } } }, "post": { "tags": ["oauth"], "operationId": "postOauthUserInfo", "responses": { "200": { "description": "OAuth userinfo response", "content": { "application/json": { "schema": {} } } }, "401": { "description": "Invalid token" } } } }, "/api/v0/main-frontend/org/{organization_slug}": { "get": { "tags": ["org"], "summary": "\nGET `/api/v0/main-frontend/org/:slug", "operationId": "getOrganizationBySlug", "parameters": [ { "name": "organization_slug", "in": "path", "description": "Organization slug", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Organization", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Organization" } } } } } } }, "/api/v0/main-frontend/organizations": { "get": { "tags": ["organizations"], "summary": "\nGET `/api/v0/main-frontend/organizations` - Returns a list of all organizations.", "operationId": "getOrganizations", "responses": { "200": { "description": "Organizations", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Organization" } } } } } } }, "post": { "tags": ["organizations"], "summary": "POST `/api/v0/main-frontend/organizations`\nCreates a new organization with the given name, slug, and visibility status.", "description": "# Request body (JSON)\n{\n \"name\": \"Example Organization\",\n \"slug\": \"example-org\",\n \"hidden\": false\n}\n\n# Response\nReturns the created organization.\n\n# Permissions\nOnly users with the `Admin` role can access this endpoint.", "operationId": "createOrganization", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrganizationCreatePayload" } } }, "required": true }, "responses": { "200": { "description": "Created organization", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/organizations/{exam_id}/fetch_org_exam": { "get": { "tags": ["organizations"], "summary": "\nGET `/api/v0/main-frontend/organizations/{exam_id}/fetch_org_exam", "operationId": "getOrganizationExamByExamId", "parameters": [ { "name": "exam_id", "in": "path", "description": "Exam id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization exam", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrgExam" } } } } } } }, "/api/v0/main-frontend/organizations/{organization_id}": { "get": { "tags": ["organizations"], "summary": "\nGET `/api/v0/main-frontend/organizations/{organization_id}` - Returns an organizations with id.", "operationId": "getOrganization", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Organization" } } } } } }, "put": { "tags": ["organizations"], "summary": "\nPUT `/api/v0/main-frontend/organizations/{organization_id}`\n\nUpdates an organization's name, hidden status, and slug.", "operationId": "updateOrganization", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrganizationUpdatePayload" } } }, "required": true }, "responses": { "200": { "description": "Organization updated" } } }, "patch": { "tags": ["organizations"], "operationId": "softDeleteOrganization", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization soft deleted" } } } }, "/api/v0/main-frontend/organizations/{organization_id}/course_exams": { "get": { "tags": ["organizations"], "summary": "\nGET `/api/v0/main-frontend/organizations/{organization_id}/course_exams` - Returns an organizations exams in CourseExam form.", "operationId": "getOrganizationCourseExams", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization course exams", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CourseExam" } } } } } } } }, "/api/v0/main-frontend/organizations/{organization_id}/courses": { "get": { "tags": ["organizations"], "summary": "\nGET `/api/v0/main-frontend/organizations/{organization_id}/courses\"` - Returns a list of all courses in a organization.", "operationId": "getOrganizationCourses", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Organization courses", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Course" } } } } } } } }, "/api/v0/main-frontend/organizations/{organization_id}/courses/active": { "get": { "tags": ["organizations"], "operationId": "getOrganizationActiveCourses", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Active organization courses", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Course" } } } } } } } }, "/api/v0/main-frontend/organizations/{organization_id}/courses/active/count": { "get": { "tags": ["organizations"], "operationId": "getOrganizationActiveCourseCount", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Active organization course count", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseCount" } } } } } } }, "/api/v0/main-frontend/organizations/{organization_id}/courses/count": { "get": { "tags": ["organizations"], "operationId": "getOrganizationCourseCount", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization course count", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseCount" } } } } } } }, "/api/v0/main-frontend/organizations/{organization_id}/courses/duplicatable": { "get": { "tags": ["organizations"], "summary": "\nGET `/api/v0/main-frontend/organizations/{organization_id}/courses/duplicatable\"` - Returns a list of all courses in a organization that the current user has permission to duplicate.", "operationId": "getOrganizationDuplicatableCourses", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Duplicatable organization courses", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Course" } } } } } } } }, "/api/v0/main-frontend/organizations/{organization_id}/exams": { "post": { "tags": ["organizations"], "summary": "\nPOST `/api/v0/main-frontend/organizations/{organization_id}/exams` - Creates new exam for the organization.", "operationId": "createOrganizationExam", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewExam" } } }, "required": true }, "responses": { "200": { "description": "Organization exam created" } } } }, "/api/v0/main-frontend/organizations/{organization_id}/image": { "put": { "tags": ["organizations"], "summary": "\nPUT `/api/v0/main-frontend/organizations/:organizations_id/image` - Sets or updates the chapter image.\n\n# Example\n\nRequest:\n```http\nPUT /api/v0/main-frontend/organizations/d332f3d9-39a5-4a18-80f4-251727693c37/image HTTP/1.1\nContent-Type: multipart/form-data\n\nBINARY_DATA\n```", "operationId": "updateOrganizationImage", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" } } } } }, "required": true }, "responses": { "200": { "description": "Updated organization", "content": { "application/json": { "schema": {} } } } } }, "delete": { "tags": ["organizations"], "summary": "\nDELETE `/api/v0/main-frontend/organizations/:organizations_id/image` - Removes the organizations image.\n\n# Example\n\nRequest:\n```http\nDELETE /api/v0/main-frontend/organizations/d332f3d9-39a5-4a18-80f4-251727693c37/image HTTP/1.1\n```", "operationId": "deleteOrganizationImage", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization image removed" } } } }, "/api/v0/main-frontend/organizations/{organization_id}/org_exams": { "get": { "tags": ["organizations"], "summary": "\nGET `/api/v0/main-frontend/organizations/{organization_id}/exams` - Returns an organizations exams in Exam form.", "operationId": "getOrganizationExams", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Organization exams", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/OrgExam" } } } } } } } }, "/api/v0/main-frontend/page_audio/{file_id}": { "delete": { "tags": ["page_audio"], "summary": "\nDELETE `/api/v0/main-frontend/page_audio/:file_id` - Removes the chapter image.\n\n# Example\n\nRequest:\n```http\nDELETE /api/v0/main-frontend/page_audio/d332f3d9-39a5-4a18-80f4-251727693c37 HTTP/1.1\n```", "operationId": "deletePageAudioFile", "parameters": [ { "name": "file_id", "in": "path", "description": "Page audio file id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Page audio deleted" } } } }, "/api/v0/main-frontend/page_audio/{page_id}": { "post": { "tags": ["page_audio"], "summary": "\nPOST `/api/v0/main-frontend/page_audio/:page_id` - Sets or updates the page audio.\n\n# Example\n\nRequest:\n```http\nPOST /api/v0/main-frontend/page_audio/d332f3d9-39a5-4a18-80f4-251727693c37 HTTP/1.1\nContent-Type: multipart/form-data\n\nBINARY_DATA\n```", "operationId": "createPageAudioFile", "parameters": [ { "name": "page_id", "in": "path", "description": "Page id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "array", "items": { "type": "integer", "format": "int32", "minimum": 0 }, "contentMediaType": "application/octet-stream" } } } } }, "required": true }, "responses": { "200": { "description": "Page audio uploaded", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/page_audio/{page_id}/files": { "get": { "tags": ["page_audio"], "summary": "\nGET `/api/v0/main-fronted/page_audio/:page_id/files` - Get a page audio files\n\nRequest: `GET /api/v0/cms/page_audio/40ca9bcf-8eaa-41ba-940e-0fd5dd0c3c02/files`", "operationId": "getPageAudioFiles", "parameters": [ { "name": "page_id", "in": "path", "description": "Page id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Page audio files", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PageAudioFile" } } } } } } } }, "/api/v0/main-frontend/pages": { "post": { "tags": ["pages"], "summary": "\nPOST `/api/v0/main-frontend/pages` - Create a new page.\n\nPlease note that this endpoint will change all the exercise and exercise task ids you've created. Make sure the use the updated ids from the response object.\n\nIf optional property front_page_of_chapter_id is set, this page will become the front page of the specified course part.\n\n# Example:\n\nRequest:\n```http\nPOST /api/v0/main-frontend/pages HTTP/1.1\nContent-Type: application/json\n\n{\n \"content\": [\n {\n \"type\": \"x\",\n \"id\": \"2a4e517d-a7d2-4d82-89fb-a1333d8d01d1\"\n }\n ],\n \"url_path\": \"/part-2/best-page\",\n \"title\": \"Hello world!\",\n \"course_id\": \"10363c5b-82b4-4121-8ef1-bae8fb42a5ce\",\n \"chapter_id\": \"2495ffa3-7ea9-4615-baa5-828023688c79\"\n}\n```", "operationId": "createPage", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewPage" } } }, "required": true }, "responses": { "200": { "description": "Created page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Page" } } } } } } }, "/api/v0/main-frontend/pages/{course_id}/all-course-pages-for-course": { "get": { "tags": ["pages"], "summary": "\nGET `/api/v0/main-frontend/pages/:course_id/all-course-pages-for-course` - Get all pages of a course", "operationId": "getCoursePages", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course pages", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Page" } } } } } } } }, "/api/v0/main-frontend/pages/{page_id}": { "delete": { "tags": ["pages"], "summary": "\nDELETE `/api/v0/main-frontend/pages/:page_id` - Delete a page, related exercises, and related exercise tasks by id.\n\n\n# Example\n\nRequest: `DELETE /api/v0/main-frontend/pages/40ca9bcf-8eaa-41ba-940e-0fd5dd0c3c02`", "operationId": "deletePage", "parameters": [ { "name": "page_id", "in": "path", "description": "Page id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted page", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Page" } } } } } } }, "/api/v0/main-frontend/pages/{page_id}/history": { "get": { "tags": ["pages"], "summary": "\nGET /api/v0/main-frontend/pages/:page_id/history", "operationId": "getPageHistory", "parameters": [ { "name": "page_id", "in": "path", "description": "Page id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Page history entries", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PageHistory" } } } } } } } }, "/api/v0/main-frontend/pages/{page_id}/history_count": { "get": { "tags": ["pages"], "summary": "\nGET /api/v0/main-frontend/pages/:page_id/history_count", "operationId": "getPageHistoryCount", "parameters": [ { "name": "page_id", "in": "path", "description": "Page id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Page history count", "content": { "text/plain": { "schema": { "type": "integer", "format": "int64" } } } } } } }, "/api/v0/main-frontend/pages/{page_id}/info": { "get": { "tags": ["pages"], "summary": "\nGET `/api/v0/main-fronted/pages/:page_id/info` - Get a pages's course id, course name, organization slug\n\nRequest: `GET /api/v0/cms/pages/40ca9bcf-8eaa-41ba-940e-0fd5dd0c3c02/info`", "operationId": "getPageInfo", "parameters": [ { "name": "page_id", "in": "path", "description": "Page id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Page info", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PageInfo" } } } } } } }, "/api/v0/main-frontend/pages/{page_id}/page-details": { "put": { "tags": ["pages"], "summary": "\nPOST `/api/v0/main-frontend/pages/:page_id/page-details` - Update pages title and url_path.", "operationId": "updatePageDetails", "parameters": [ { "name": "page_id", "in": "path", "description": "Page id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PageDetailsUpdate" } } }, "required": true }, "responses": { "200": { "description": "Updated page details", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/pages/{page_id}/restore": { "post": { "tags": ["pages"], "summary": "\nPOST /api/v0/main-frontend/pages/:page_id/restore", "operationId": "restorePageHistory", "parameters": [ { "name": "page_id", "in": "path", "description": "Page id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HistoryRestoreData" } } }, "required": true }, "responses": { "200": { "description": "Restored history id", "content": { "text/plain": { "schema": { "type": "string", "format": "uuid" } } } } } } }, "/api/v0/main-frontend/playground-views/grading/{websocket_id}": { "post": { "tags": ["playground-views"], "summary": "playground-views passes a URL pointing to this route to an exercise service when sending submissions so that if\nthe service has an update for a pending grading, it will be sent here and passed on to through the websocket", "operationId": "receivePlaygroundGrading", "parameters": [ { "name": "websocket_id", "in": "path", "description": "Playground websocket id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseTaskGradingResult" } } }, "required": true }, "responses": { "200": { "description": "Grading forwarded to websocket client" } } } }, "/api/v0/main-frontend/playground-views/ws": { "get": { "tags": ["playground-views"], "summary": "Starts a new websocket connection.", "operationId": "getPlaygroundViewsWebsocket", "responses": { "101": { "description": "WebSocket connection upgraded" } } } }, "/api/v0/main-frontend/playground_examples": { "get": { "tags": ["playground-examples"], "summary": "\nGET `/api/v0/main-frontend/playground_examples` - Returns all playground examples that are not deleted.", "operationId": "getPlaygroundExamples", "responses": { "200": { "description": "Playground examples", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PlaygroundExample" } } } } } } }, "put": { "tags": ["playground-examples"], "summary": "\nPUT `/api/v0/main-frontend/playground_examples` - Updates existing playground example.", "operationId": "updatePlaygroundExample", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaygroundExample" } } }, "required": true }, "responses": { "200": { "description": "Updated playground example", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaygroundExample" } } } } } }, "post": { "tags": ["playground-examples"], "summary": "\nPOST `/api/v0/main-frontend/playground_examples` - Saves a playground example.", "operationId": "createPlaygroundExample", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaygroundExampleData" } } }, "required": true }, "responses": { "200": { "description": "Created playground example", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaygroundExample" } } } } } } }, "/api/v0/main-frontend/playground_examples/{playground_example_id}": { "delete": { "tags": ["playground-examples"], "summary": "\nDELETE `/api/v0/main-frontend/playground_examples` - Deletes a playground example if exists.", "operationId": "deletePlaygroundExample", "parameters": [ { "name": "playground_example_id", "in": "path", "description": "Playground example id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Deleted playground example", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaygroundExample" } } } } } } }, "/api/v0/main-frontend/proposed-edits/course/{course_id}": { "get": { "tags": ["proposed_edits"], "summary": "\nGET `/api/v0/main-frontend/proposed-edits/course/:id?pending=true` - Returns feedback for the given course.", "operationId": "getEditProposals", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "pending", "in": "query", "description": "Whether to fetch pending proposals", "required": true, "schema": { "type": "boolean" } }, { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Edit proposals", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PageProposal" } } } } } } } }, "/api/v0/main-frontend/proposed-edits/course/{course_id}/count": { "get": { "tags": ["proposed_edits"], "summary": "\nGET `/api/v0/main-frontend/proposed-edits/course/:id/count` - Returns the amount of feedback for the given course.", "operationId": "getEditProposalCount", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Edit proposal counts", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProposalCount" } } } } } } }, "/api/v0/main-frontend/proposed-edits/process-edit-proposal": { "post": { "tags": ["proposed_edits"], "summary": "\nPOST `/api/v0/main-frontend/proposed-edits/process-edit-proposal` - Processes the given edit proposal.", "operationId": "processEditProposal", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EditProposalInfo" } } }, "required": true }, "responses": { "200": { "description": "Processed edit proposal" } } } }, "/api/v0/main-frontend/regradings": { "get": { "tags": ["regradings"], "summary": "\nGET `/api/v0/main-frontend/regradings` - Returns a paginated list of all the regradings.", "operationId": "getRegradings", "parameters": [ { "name": "page", "in": "query", "description": "Page number", "required": false, "schema": { "type": "integer", "format": "int64" } }, { "name": "limit", "in": "query", "description": "Page size", "required": false, "schema": { "type": "integer", "format": "int64" } } ], "responses": { "200": { "description": "Regradings", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Regrading" } } } } } } }, "post": { "tags": ["regradings"], "summary": "\nPOST `/api/v0/main-frontend/regradings` - Creates a new regrading for the supplied exercise task submission ids and returns the new regrading id.", "operationId": "createRegrading", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewRegrading" } } }, "required": true }, "responses": { "200": { "description": "Created regrading id", "content": { "text/plain": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/regradings/count": { "get": { "tags": ["regradings"], "summary": "\nGET `/api/v0/main-frontend/regradings/count` - Counts regradings", "operationId": "getRegradingsCount", "responses": { "200": { "description": "Regradings count", "content": { "text/plain": { "schema": { "type": "integer", "format": "int64" } } } } } } }, "/api/v0/main-frontend/regradings/{regrading_id}": { "get": { "tags": ["regradings"], "summary": "\nGET `/api/v0/main-frontend/regradings/{id}` - Returns relevant information about a regrading.", "operationId": "getRegradingInfo", "parameters": [ { "name": "regrading_id", "in": "path", "description": "Regrading id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Regrading info", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegradingInfo" } } } } } } }, "/api/v0/main-frontend/roles": { "get": { "tags": ["roles"], "summary": "\n * GET /api/v0/main-frontend/roles - Get all roles for the given domain.", "operationId": "getRoles", "parameters": [ { "name": "global", "in": "query", "description": "Global domain", "required": false, "schema": { "type": "boolean" } }, { "name": "organization_id", "in": "query", "description": "Organization id", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "course_id", "in": "query", "description": "Course id", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "course_instance_id", "in": "query", "description": "Course instance id", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "exam_id", "in": "query", "description": "Exam id", "required": false, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Roles", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/RoleUser" } } } } } } } }, "/api/v0/main-frontend/roles/add": { "post": { "tags": ["roles"], "summary": "\n * POST /api/v0/main-frontend/roles/add - Give a role to a user.", "operationId": "addRole", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RoleInfo" } } }, "required": true }, "responses": { "200": { "description": "Role added" } } } }, "/api/v0/main-frontend/roles/pending": { "get": { "tags": ["roles"], "summary": "\n * GET /api/v0/main-frontend/roles - Get all pending roles for the given domain.", "operationId": "getPendingRoles", "parameters": [ { "name": "global", "in": "query", "description": "Global domain", "required": false, "schema": { "type": "boolean" } }, { "name": "organization_id", "in": "query", "description": "Organization id", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "course_id", "in": "query", "description": "Course id", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "course_instance_id", "in": "query", "description": "Course instance id", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "exam_id", "in": "query", "description": "Exam id", "required": false, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Pending roles", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PendingRole" } } } } } } } }, "/api/v0/main-frontend/roles/remove": { "post": { "tags": ["roles"], "summary": "\n * POST /api/v0/main-frontend/roles/remove - Remove a role from a user.", "operationId": "removeRole", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RoleInfo" } } }, "required": true }, "responses": { "200": { "description": "Role removed" } } } }, "/api/v0/main-frontend/shared-submissions": { "get": { "tags": ["shared_submissions"], "summary": "\nGET `/api/v0/main-frontend/shared-submissions` - Lists the shares the current user has minted,\nnewest first, so they can be reviewed and withdrawn.", "operationId": "listOwnSubmissionShares", "responses": { "200": { "description": "The caller's live shares, newest first", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmissionShare" } } } } } } } }, "/api/v0/main-frontend/shared-submissions/of-submission/{submission_id}": { "delete": { "tags": ["shared_submissions"], "summary": "\nDELETE `/api/v0/main-frontend/shared-submissions/of-submission/{submission_id}` - Withdraws every\nshare the current user has minted for one submission.", "operationId": "revokeSubmissionSharesOfSubmission", "parameters": [ { "name": "submission_id", "in": "path", "description": "Exercise slide submission id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "How many shares were withdrawn", "content": { "text/plain": { "schema": { "type": "integer", "format": "int64" } } } } } } }, "/api/v0/main-frontend/shared-submissions/{token}": { "get": { "tags": ["shared_submissions"], "summary": "\nGET `/api/v0/main-frontend/shared-submissions/{token}` - Returns the data needed to\nrender a shared submission.\n\nThe `token` is the unguessable share id minted by the client share endpoint. Login is\nrequired, but holding the token is the only capability needed to view the submission —\nno teacher or course role.", "operationId": "getSharedSubmissionInfo", "parameters": [ { "name": "token", "in": "path", "description": "Submission share token", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Data needed to render the shared submission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExerciseSlideSubmissionInfo" } } } } } }, "delete": { "tags": ["shared_submissions"], "summary": "\nDELETE `/api/v0/main-frontend/shared-submissions/{token}` - Withdraws one share, after which the\nlink stops resolving.\n\nOnly the share's creator may revoke it: holding the token is authority to view, not to withdraw.\nRevoking an unknown or already-revoked share is a no-op, reported as `false`.", "operationId": "revokeSubmissionShare", "parameters": [ { "name": "token", "in": "path", "description": "Submission share token", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Whether a live share was withdrawn", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/status/cronjobs": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/cronjobs`\n\nReturns the status of all CronJobs in the current namespace.", "operationId": "getStatusCronjobs", "responses": { "200": { "description": "Cronjobs", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CronJobInfo" } } } } } } } }, "/api/v0/main-frontend/status/deployments": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/deployments`\n\nReturns the status of all Deployments in the current namespace.", "operationId": "getStatusDeployments", "responses": { "200": { "description": "Deployments", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/DeploymentInfo" } } } } } } } }, "/api/v0/main-frontend/status/events": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/events`\n\nReturns the status of all Events in the current namespace.", "operationId": "getStatusEvents", "responses": { "200": { "description": "Events", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/EventInfo" } } } } } } } }, "/api/v0/main-frontend/status/health": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/health`\n\nReturns detailed system health status with issues list (admin only).", "operationId": "getStatusHealth", "responses": { "200": { "description": "Detailed system health", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SystemHealthStatus" } } } } } } }, "/api/v0/main-frontend/status/ingresses": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/ingresses`\n\nReturns the status of all Ingresses in the current namespace.", "operationId": "getStatusIngresses", "responses": { "200": { "description": "Ingresses", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/IngressInfo" } } } } } } } }, "/api/v0/main-frontend/status/jobs": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/jobs`\n\nReturns the status of all Jobs in the current namespace.", "operationId": "getStatusJobs", "responses": { "200": { "description": "Jobs", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/JobInfo" } } } } } } } }, "/api/v0/main-frontend/status/pod-disruption-budgets": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/pod-disruption-budgets`\n\nReturns the status of all PodDisruptionBudgets in the current namespace.", "operationId": "getStatusPodDisruptionBudgets", "responses": { "200": { "description": "Pod disruption budgets", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PodDisruptionBudgetInfo" } } } } } } } }, "/api/v0/main-frontend/status/pods": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/pods`\n\nReturns the status of all Pods in the current namespace.", "operationId": "getStatusPods", "responses": { "200": { "description": "Pods", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PodInfo" } } } } } } } }, "/api/v0/main-frontend/status/pods/{pod_name}/logs": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/pods/{pod_name}/logs`\n\nReturns logs from a specific pod.\n\nQuery parameters:\n- container: Optional - Container name (if pod has multiple containers)\n- tail: Optional - Number of lines to tail from the end (default: 1000, max: 10000)", "operationId": "getStatusPodLogs", "parameters": [ { "name": "pod_name", "in": "path", "description": "Pod name", "required": true, "schema": { "type": "string" } }, { "name": "container", "in": "query", "description": "Container name", "required": false, "schema": { "type": "string" } }, { "name": "tail", "in": "query", "description": "Number of log lines", "required": false, "schema": { "type": "integer", "format": "int64", "minimum": 0 } } ], "responses": { "200": { "description": "Pod logs", "content": { "text/plain": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/status/services": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/services`\n\nReturns the status of all Services in the current namespace.", "operationId": "getStatusServices", "responses": { "200": { "description": "Services", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ServiceInfo" } } } } } } } }, "/api/v0/main-frontend/status/system-health": { "get": { "tags": ["status"], "summary": "\nGET `/api/v0/main-frontend/status/system-health` Returns a boolean indicating whether the system is healthy.\n\nUses the same underlying checking logic as the detailed health endpoint.\nUnauthenticated users get a boolean. Authenticated admins get error details on failure.", "operationId": "getStatusSystemHealth", "responses": { "200": { "description": "System health", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/teacher-grading-decisions": { "post": { "tags": ["teacher_grading_decisions"], "summary": "\nPOST `/api/v0/main-frontend/teacher-grading-decisions` - Creates a new teacher grading decision, overriding the points a user has received from an exercise.", "operationId": "createTeacherGradingDecision", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NewTeacherGradingDecision" } } }, "required": true }, "responses": { "200": { "description": "Teacher grading decision created", "content": { "application/json": { "schema": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/UserExerciseState" } ] } } } } } } }, "/api/v0/main-frontend/time": { "get": { "tags": ["time"], "summary": "\nGET `/api/v0/main-frontend/time` Returns the server's current UTC time as an RFC3339 timestamp string.\n\nResponse body example:\n`\"2026-02-18T12:34:56.789Z\"`", "operationId": "getCurrentTime", "responses": { "200": { "description": "Current server time", "content": { "text/plain": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/user-details/bulk-user-details": { "post": { "tags": ["user-details"], "summary": "\nPOST `/api/v0/main-frontend/user-details/bulk-user-details` - Get user details for a list of user IDs with course permission check\nOnly returns user details for users who are actually enrolled in the specified course", "operationId": "getBulkUserDetails", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkUserDetailsRequest" } } }, "required": true }, "responses": { "200": { "description": "Bulk user details", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserDetail" } } } } } } } }, "/api/v0/main-frontend/user-details/search-by-email": { "post": { "tags": ["user-details"], "summary": "\nGET `/api/v0/main-frontend/user-details/search-by-email` - Allows to search user by their email", "operationId": "searchUserDetailsByEmail", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchRequest" } } }, "required": true }, "responses": { "200": { "description": "User details search results", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserDetail" } } } } } } } }, "/api/v0/main-frontend/user-details/search-by-other-details": { "post": { "tags": ["user-details"], "summary": "\nGET `/api/v0/main-frontend/user-details/search-by-other-details` - Allows to search user by their names etc.", "operationId": "searchUserDetailsByOtherDetails", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchRequest" } } }, "required": true }, "responses": { "200": { "description": "User details search results", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserDetail" } } } } } } } }, "/api/v0/main-frontend/user-details/search-fuzzy-match": { "post": { "tags": ["user-details"], "summary": "\nGET `/api/v0/main-frontend/user-details/search-fuzzy-match` - Allows to find the right user details in cases where there is a small typing error in the search query", "operationId": "searchUserDetailsFuzzyMatch", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchRequest" } } }, "required": true }, "responses": { "200": { "description": "User details fuzzy search results", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserDetail" } } } } } } } }, "/api/v0/main-frontend/user-details/update-user-info": { "post": { "tags": ["user-details"], "summary": "\nPOST `/api/v0/main-frontend/user-details/update-user-info` - Updates the users information such as email, name, country and email communication consent", "operationId": "updateUserInfo", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserInfoPayload" } } }, "required": true }, "responses": { "200": { "description": "Updated user details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserDetail" } } } } } } }, "/api/v0/main-frontend/user-details/user-by-courses": { "post": { "tags": ["user-details"], "summary": "\nPOST `/api/v0/main-frontend/user-details/user-by-courses` - Find user details by user id with multi-course permission check\nReturns user details if the user has permission to view user details through any of the specified courses", "operationId": "getUserDetailsByCourses", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserDetailsRequest" } } }, "required": true }, "responses": { "200": { "description": "User details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserDetail" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": {} } } }, "404": { "description": "User details not found", "content": { "application/json": { "schema": {} } } }, "500": { "description": "Internal server error", "content": { "application/json": { "schema": {} } } } } } }, "/api/v0/main-frontend/user-details/user-details-for-user": { "get": { "tags": ["user-details"], "summary": "\nGET `/api/v0/main-frontend/user-details/user-details-for-user` - Get authenticated user's own details", "operationId": "getUserDetailsForAuthenticatedUser", "responses": { "200": { "description": "Authenticated user details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserDetail" } } } } } } }, "/api/v0/main-frontend/user-details/users-ip-country": { "get": { "tags": ["user-details"], "operationId": "getUsersIpCountry", "responses": { "200": { "description": "Country inferred from request IP", "content": { "text/plain": { "schema": { "type": "string" } } } } } } }, "/api/v0/main-frontend/user-details/{course_id}/get-users-by-course-id": { "get": { "tags": ["user-details"], "summary": "\nGET `/api/v0/main-frontend/user-details/get-users-by-course-id` - Get user details of users that are in the course", "operationId": "getUsersByCourseIdForUserDetails", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Users by course id", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserDetail" } } } } } } } }, "/api/v0/main-frontend/user-details/{course_id}/user/{user_id}": { "get": { "tags": ["user-details"], "summary": "\nGET `/api/v0/main-frontend/user-details/{course_id}/user/{user_id}` - Find user details by user id with course permission check\nOnly returns user details if the user is enrolled in the specified course", "operationId": "getUserDetailsByCourseAndUserId", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserDetail" } } } } } } }, "/api/v0/main-frontend/users/change-password": { "post": { "tags": ["users"], "operationId": "changeUserPassword", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChangePasswordData" } } }, "required": true }, "responses": { "200": { "description": "Password change status", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/users/get-user-research-consent": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/get-user-research-consent` - Gets users research consent.", "operationId": "getUserResearchConsent", "responses": { "200": { "description": "User research consent", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserResearchConsent" } } } } } } }, "/api/v0/main-frontend/users/my-certificates": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/my-certificates` - Every certificate the authenticated user holds.\n\nNo user id parameter, so it cannot be pointed at another account. Anyone holding a certificate's\nverification id can already fetch its image; this only lists which ones are the caller's.", "operationId": "getMyCertificates", "responses": { "200": { "description": "The authenticated user's certificates", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserCertificate" } } } } } } } }, "/api/v0/main-frontend/users/my-courses": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/my-courses` - Gets all the courses the user has either started or gotten a permission to.", "operationId": "getMyCourses", "responses": { "200": { "description": "Courses for authenticated user", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/MyCourse" } } } } } } } }, "/api/v0/main-frontend/users/my-courses/{course_id}/hide": { "post": { "tags": ["users"], "summary": "\nPOST `/api/v0/main-frontend/users/my-courses/:course_id/hide` - Hides a course from the\nauthenticated user's \"My courses\" list.", "operationId": "hideCourseFromMyCourses", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course hidden from the user's my-courses list" } } } }, "/api/v0/main-frontend/users/my-courses/{course_id}/unhide": { "post": { "tags": ["users"], "summary": "\nPOST `/api/v0/main-frontend/users/my-courses/:course_id/unhide` - Puts a previously hidden course\nback into the authenticated user's \"My courses\" list.", "operationId": "unhideCourseFromMyCourses", "parameters": [ { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Course restored to the user's my-courses list" } } } }, "/api/v0/main-frontend/users/my-studies": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/my-studies` - The authenticated user's own study record: every\ncourse they are enrolled in, its modules, and their completions.\n\nNo user id parameter, so it cannot be pointed at another account. The teacher/admin equivalent is\n`getUserCourseEnrollments`.", "operationId": "getMyStudies", "responses": { "200": { "description": "The authenticated user's study record", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MyStudies" } } } } } } }, "/api/v0/main-frontend/users/reset-password": { "post": { "tags": ["users"], "operationId": "resetUserPassword", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResetPasswordData" } } }, "required": true }, "responses": { "200": { "description": "Password reset status", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/users/reset-password-token-status": { "post": { "tags": ["users"], "operationId": "getResetPasswordTokenStatus", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResetPasswordTokenPayload" } } }, "required": true }, "responses": { "200": { "description": "Reset password token validity", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/users/send-reset-password-email": { "post": { "tags": ["users"], "operationId": "sendResetPasswordEmail", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailData" } } }, "required": true }, "responses": { "200": { "description": "Reset password email accepted", "content": { "text/plain": { "schema": { "type": "boolean" } } } } } } }, "/api/v0/main-frontend/users/user-research-consents": { "post": { "tags": ["users"], "summary": "\nPOST `/api/v0/main-frontend/users/user-research-consents` - Adds a research consent for a student.", "operationId": "createUserResearchConsent", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConsentData" } } }, "required": true }, "responses": { "200": { "description": "User research consent", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserResearchConsent" } } } } } } }, "/api/v0/main-frontend/users/user-research-form-question-answers": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/get-user-research-consents` - Gets all users research consents for a course specific research form.", "operationId": "getUserResearchFormQuestionAnswers", "responses": { "200": { "description": "Research form answers for user", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ResearchFormQuestionAnswer" } } } } } } } }, "/api/v0/main-frontend/users/{user_id}": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/:id`", "operationId": "getUser", "parameters": [ { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } } } } }, "/api/v0/main-frontend/users/{user_id}/course-enrollments": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/:id/course-enrollments`", "operationId": "getUserCourseEnrollments", "parameters": [ { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User course enrollments", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseEnrollmentsInfo" } } } } } } }, "/api/v0/main-frontend/users/{user_id}/courses/{course_id}/submission-times": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/:id/courses/:course_id/submission-times` - A user's exercise\nsubmission times in a course, each tagged with its exercise and module. Teacher/admin (global) view.", "operationId": "getUserCourseSubmissionTimes", "parameters": [ { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } }, { "name": "course_id", "in": "path", "description": "Course id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User course submission times", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserCourseSubmissionTime" } } } } } } } }, "/api/v0/main-frontend/users/{user_id}/roles": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/:id/roles` - All roles held by a user, across scopes. Teacher/admin\n(global) view; used to label the account (e.g. staff/teacher) on the user-details page.", "operationId": "getUserRoles", "parameters": [ { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User roles across scopes", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Role" } } } } } } } }, "/api/v0/main-frontend/users/{user_id}/suspected-cheaters": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/:id/suspected-cheaters` - Cross-course suspected-cheater records for\na user, each paired with the course's applicable duration threshold. Teacher/admin (global) view;\nread-only (confirm/dismiss happen on the per-course cheaters page).", "operationId": "getUserSuspectedCheaters", "parameters": [ { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User suspected-cheater records across courses", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserSuspectedCheaterInfo" } } } } } } } }, "/api/v0/main-frontend/users/{user_id}/user-reset-exercise-logs": { "get": { "tags": ["users"], "summary": "\nGET `/api/v0/main-frontend/users/:id/user-reset-exercise-logs` - Get all logs of reset exercises for a user", "operationId": "getUserResetExerciseLogs", "parameters": [ { "name": "user_id", "in": "path", "description": "User id", "required": true, "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "User reset exercise logs", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseResetLog" } } } } } } } } }, "components": { "schemas": { "AccountLinkingFailureDomain": { "type": "object", "description": "Hard send failures grouped by recipient domain.", "required": ["domain", "count"], "properties": { "count": { "type": "integer", "format": "int64" }, "domain": { "type": "string" } } }, "AccountLinkingFunnel": { "type": "object", "description": "The account-linking funnel. The `_last_run` steps come from counters the discovery phase overwrites\nwhole, the `_in_window` ones from the window: there is no single denominator.", "required": [ "persons_discovered_last_run", "already_linked_last_run", "mails_claimed_in_window", "mails_sent_in_window", "numbers_claimed_in_window", "manual_links_in_window", "suppressed_by_dedup_last_run", "suppressed_by_rate_cap_last_run", "no_address_in_study_registry_last_run", "fast_tracked_in_window", "fast_tracked_last_run" ], "properties": { "already_linked_last_run": { "type": "integer", "format": "int64" }, "fast_tracked_in_window": { "type": "integer", "format": "int64", "description": "The branch that skips the mail entirely: discovered persons linked straight away because the\nstudy registry holds a verified account address for them. A terminal branch off `discovered`,\nnot a stage every person passes through." }, "fast_tracked_last_run": { "type": "integer", "format": "int64" }, "mails_claimed_in_window": { "type": "integer", "format": "int64" }, "mails_sent_in_window": { "type": "integer", "format": "int64" }, "manual_links_in_window": { "type": "integer", "format": "int64", "description": "Never folded into the claimed count: an admin's judgement is not a claim." }, "no_address_in_study_registry_last_run": { "type": "integer", "format": "int64" }, "numbers_claimed_in_window": { "type": "integer", "format": "int64" }, "persons_discovered_last_run": { "type": "integer", "format": "int64" }, "suppressed_by_dedup_last_run": { "type": "integer", "format": "int64" }, "suppressed_by_rate_cap_last_run": { "type": "integer", "format": "int64" } } }, "AccountLinkingRealisationCounters": { "type": "object", "required": [ "course_id", "course_name", "course_module_id", "course_unit_realisation_id", "consecutive_listing_failures" ], "properties": { "already_linked_count": { "type": ["integer", "null"], "format": "int32" }, "consecutive_listing_failures": { "type": "integer", "format": "int32" }, "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "course_name": { "type": "string" }, "course_unit_realisation_id": { "type": "string" }, "fast_track_skipped_account_has_number_count": { "type": ["integer", "null"], "format": "int32" }, "fast_track_skipped_name_mismatch_count": { "type": ["integer", "null"], "format": "int32", "description": "A rise here is the only early warning of a university address reissued to a different person." }, "fast_track_skipped_no_account_count": { "type": ["integer", "null"], "format": "int32" }, "fast_track_skipped_stale_verification_count": { "type": ["integer", "null"], "format": "int32" }, "fast_track_skipped_unlinked_before_count": { "type": ["integer", "null"], "format": "int32" }, "fast_track_skipped_unverified_count": { "type": ["integer", "null"], "format": "int32", "description": "Matched an account that has never proved the address. The population an email-verification\ncampaign would convert." }, "fast_tracked_count": { "type": ["integer", "null"], "format": "int32" }, "label": { "type": ["string", "null"] }, "last_listed_at": { "type": ["string", "null"], "format": "date-time", "description": "When the counters below were collected. Not the last attempt: a failing realisation keeps the\nlast roster that arrived." }, "last_listing_attempted_at": { "type": ["string", "null"], "format": "date-time" }, "last_listing_error": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode", "description": "Set while the listing attempts since `last_listed_at` are failing, so an empty course and an\nunreachable one do not read alike." } ] }, "listed_person_count": { "type": ["integer", "null"], "format": "int32" }, "mailed_count": { "type": ["integer", "null"], "format": "int32" }, "no_address_count": { "type": ["integer", "null"], "format": "int32", "description": "Persons the registry holds no address for: the one population no remedy here can reach." }, "suppressed_by_dedup_count": { "type": ["integer", "null"], "format": "int32" }, "suppressed_by_rate_cap_count": { "type": ["integer", "null"], "format": "int32" }, "uh_course_code": { "type": ["string", "null"] } } }, "AccountLinkingSendStatusTotals": { "type": "object", "required": ["queued", "retrying", "sent", "send_failed"], "properties": { "queued": { "type": "integer", "format": "int64" }, "retrying": { "type": "integer", "format": "int64" }, "send_failed": { "type": "integer", "format": "int64" }, "sent": { "type": "integer", "format": "int64" } } }, "AccountLinkingStaleAddress": { "type": "object", "description": "A person mailed to the cap for one course whose number was never claimed.", "required": [ "student_number", "sisu_person_id", "course_id", "course_name", "mail_count", "first_sent_at", "last_sent_at", "addresses", "send_statuses" ], "properties": { "addresses": { "type": "array", "items": { "type": "string" }, "description": "In full, newest last, one per mail." }, "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "first_sent_at": { "type": "string", "format": "date-time" }, "last_sent_at": { "type": "string", "format": "date-time" }, "mail_count": { "type": "integer", "format": "int64" }, "send_statuses": { "type": "array", "items": { "$ref": "#/components/schemas/EmailSendStatus" } }, "sisu_person_id": { "type": "string" }, "student_number": { "type": "string" } } }, "AccountLinkingStats": { "type": "object", "required": [ "window_secs", "funnel", "send_status_totals", "hard_failure_domains", "realisations", "stale_addresses", "links_total_by_method", "links_in_window_by_method", "waiting_for_student_number_count", "max_mails_per_person_and_course", "quiet_period_secs" ], "properties": { "funnel": { "$ref": "#/components/schemas/AccountLinkingFunnel" }, "hard_failure_domains": { "type": "array", "items": { "$ref": "#/components/schemas/AccountLinkingFailureDomain" } }, "links_in_window_by_method": { "type": "array", "items": { "$ref": "#/components/schemas/VerifiedStudentNumberMethodTotal" } }, "links_total_by_method": { "type": "array", "items": { "$ref": "#/components/schemas/VerifiedStudentNumberMethodTotal" } }, "max_mails_per_person_and_course": { "type": "integer", "format": "int64" }, "quiet_period_secs": { "type": "integer", "format": "int64" }, "realisations": { "type": "array", "items": { "$ref": "#/components/schemas/AccountLinkingRealisationCounters" } }, "send_status_totals": { "$ref": "#/components/schemas/AccountLinkingSendStatusTotals" }, "stale_addresses": { "type": "array", "items": { "$ref": "#/components/schemas/AccountLinkingStaleAddress" } }, "waiting_for_student_number_count": { "type": "integer", "format": "int64", "description": "Accounts with a consented completion still waiting for a number." }, "window_secs": { "type": "integer", "format": "int64" } } }, "ActivityProgress": { "type": "string", "description": "\nIndicates what is the user's completion status for a exercise.\n\nAs close as possible to LTI's activity progress for compatibility: .", "enum": ["Initialized", "Started", "InProgress", "Submitted", "Completed"] }, "AddPlanMemberRequest": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "format": "email" } } }, "AdminBulkTransitionPayload": { "type": "object", "required": ["to_state", "credit_registration_ids", "reason"], "properties": { "credit_registration_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "reason": { "type": "string" }, "to_state": { "$ref": "#/components/schemas/AdminCreditRegistrationTransitionTarget" } } }, "AdminBulkTransitionResult": { "type": "object", "required": ["applied_count", "skipped", "not_found_count", "max_rows_per_call"], "properties": { "applied_count": { "type": "integer", "format": "int64" }, "max_rows_per_call": { "type": "integer", "format": "int64" }, "not_found_count": { "type": "integer", "format": "int64", "description": "Distinct selected ids naming no live row." }, "skipped": { "type": "array", "items": { "$ref": "#/components/schemas/AdminBulkTransitionSkipCount" } } } }, "AdminBulkTransitionSkip": { "type": "string", "description": "Why a selected row was left alone.", "enum": ["superseded", "submission_uncertain", "without_consent"] }, "AdminBulkTransitionSkipCount": { "type": "object", "required": ["reason", "count"], "properties": { "count": { "type": "integer", "format": "int64" }, "reason": { "$ref": "#/components/schemas/AdminBulkTransitionSkip" } } }, "AdminCreditRegistrationDetails": { "type": "object", "required": [ "registration", "attempts", "events", "suotar_api_calls", "actions", "linking_emails", "notification_emails" ], "properties": { "actions": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationAdminActionRecord" }, "description": "Admin and teacher actions targeting this row." }, "attempts": { "type": "array", "items": { "$ref": "#/components/schemas/AdminCreditRegistrationRow" }, "description": "Every attempt for the same completion, newest first, this one included." }, "consent_given": { "type": ["boolean", "null"] }, "consent_withdrawn_at": { "type": ["string", "null"], "format": "date-time" }, "events": { "type": "array", "items": { "$ref": "#/components/schemas/AdminCreditRegistrationEvent" } }, "linking_emails": { "type": "array", "items": { "$ref": "#/components/schemas/AdminLinkingEmail" }, "description": "Every mail addressed to this person, on any course." }, "not_improved_attainment": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/NotImprovedAttainment", "description": "The grade the registry already held, for a row it declined as no improvement. The row's own\ngrade is what we sent." } ] }, "notification_emails": { "type": "array", "items": { "$ref": "#/components/schemas/AdminNotificationEmail" }, "description": "The terminal-state mails queued for this row, with the same send status the student and the\nteacher are shown." }, "registration": { "$ref": "#/components/schemas/AdminCreditRegistrationRow" }, "suotar_api_calls": { "type": "array", "items": { "$ref": "#/components/schemas/AdminSuotarApiCall" }, "description": "The calls the timeline refers to, newest first." } } }, "AdminCreditRegistrationEvent": { "type": "object", "required": ["id", "created_at", "kind"], "properties": { "actor_user_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "details": { "description": "The `{request, response}` pair, scrubbed at write time: names, student numbers and email\naddresses read `[redacted]` while their keys survive. The values we sent are on the row." }, "error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "from_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "id": { "type": "string", "format": "uuid" }, "kind": { "$ref": "#/components/schemas/CreditRegistrationEventKind" }, "message": { "type": ["string", "null"], "description": "Our own wording, written by the pipeline or by whoever acted." }, "suotar_api_call_id": { "type": ["string", "null"], "format": "uuid" }, "to_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] } } }, "AdminCreditRegistrationRow": { "type": "object", "required": [ "id", "created_at", "user_id", "course_id", "course_name", "course_module_id", "course_instance_id", "course_module_completion_id", "completion_date", "state", "state_entered_at", "needs_admin_attention", "next_attempt_at", "request_item_id", "submit_retry_count", "verify_attempt_count", "attempt_number", "superseded" ], "properties": { "attempt_number": { "type": "integer", "format": "int32" }, "completion_date": { "type": "string", "format": "date-time" }, "course_id": { "type": "string", "format": "uuid" }, "course_instance_id": { "type": "string", "format": "uuid" }, "course_module_completion_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "course_name": { "type": "string" }, "created_at": { "type": "string", "format": "date-time" }, "credits": { "type": ["number", "null"], "format": "float" }, "email": { "type": ["string", "null"], "description": "In full: masking it would leave support unable to answer the question they were asked." }, "error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "first_name": { "type": ["string", "null"] }, "grade_id": { "type": ["string", "null"] }, "grade_scale_id": { "type": ["string", "null"] }, "id": { "type": "string", "format": "uuid" }, "last_attempt_at": { "type": ["string", "null"], "format": "date-time" }, "last_name": { "type": ["string", "null"] }, "needs_admin_attention": { "type": "boolean" }, "next_attempt_at": { "type": "string", "format": "date-time" }, "registered_at": { "type": ["string", "null"], "format": "date-time" }, "request_item_id": { "type": "string" }, "selected_enrolment_id": { "type": ["string", "null"] }, "sisu_attainment_id": { "type": ["string", "null"] }, "sisu_person_id": { "type": ["string", "null"] }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" }, "state_entered_at": { "type": "string", "format": "date-time" }, "student_number": { "type": ["string", "null"], "description": "Frozen on the row before it was sent, so it is what we actually submitted." }, "submit_retry_count": { "type": "integer", "format": "int32" }, "submitted_at": { "type": ["string", "null"], "format": "date-time" }, "submitted_attainment_id": { "type": ["string", "null"] }, "superseded": { "type": "boolean" }, "superseded_by_id": { "type": ["string", "null"], "format": "uuid" }, "terminal_at": { "type": ["string", "null"], "format": "date-time" }, "uh_course_code": { "type": ["string", "null"] }, "user_id": { "type": "string", "format": "uuid" }, "verified_student_number": { "type": ["string", "null"], "description": "The account's link now, which is not always the number frozen on the row." }, "verified_student_number_at": { "type": ["string", "null"], "format": "date-time" }, "verified_student_number_via": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/StudentNumberVerificationMethod", "description": "`admin_manual` means support established the link rather than the student proving it." } ] }, "verify_attempt_count": { "type": "integer", "format": "int32" } } }, "AdminCreditRegistrationTransitionTarget": { "type": "string", "description": "What an admin may move a row to; everything else is the pipeline's to decide.", "enum": ["ready_to_submit", "cancelled", "clear_needs_admin_attention", "check_now"] }, "AdminCreditRegistrationsPage": { "type": "object", "required": ["data", "total_count", "total_pages"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AdminCreditRegistrationRow" } }, "total_count": { "type": "integer", "format": "int64" }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 } } }, "AdminLinkingEmail": { "type": "object", "required": [ "id", "course_id", "student_number", "sisu_person_id", "emailed_to", "claimed_at", "send_status" ], "properties": { "claimed_at": { "type": "string", "format": "date-time" }, "course_id": { "type": "string", "format": "uuid" }, "emailed_to": { "type": "string", "description": "In full." }, "id": { "type": "string", "format": "uuid" }, "send_status": { "$ref": "#/components/schemas/EmailSendStatusReport" }, "sisu_person_id": { "type": "string" }, "student_number": { "type": "string" }, "token_claimed_by_user_id": { "type": ["string", "null"], "format": "uuid" }, "token_expires_at": { "type": ["string", "null"], "format": "date-time" }, "token_used_at": { "type": ["string", "null"], "format": "date-time" } } }, "AdminManualLinkOutcome": { "type": "string", "enum": [ "linked", "student_number_not_found", "preview_mismatch", "already_linked_to_another_account", "already_linked_to_this_account", "study_registry_unavailable" ] }, "AdminManuallyLinkStudentNumberPayload": { "type": "object", "required": ["user_id", "student_number", "sisu_person_id", "reason"], "properties": { "reason": { "type": "string" }, "sisu_person_id": { "type": "string", "description": "From the preview. Re-resolved on arrival, and a mismatch is refused, so a typo cannot mint a\nlink to somebody else." }, "student_number": { "type": "string" }, "user_id": { "type": "string", "format": "uuid" } } }, "AdminManuallyLinkStudentNumberResult": { "type": "object", "required": ["outcome", "affected_registration_count"], "properties": { "affected_registration_count": { "type": "integer", "format": "int64", "description": "Registrations the link unblocked." }, "outcome": { "$ref": "#/components/schemas/AdminManualLinkOutcome" }, "verified_student_number_id": { "type": ["string", "null"], "format": "uuid" } } }, "AdminMaterializePayload": { "type": "object", "properties": { "reason": { "type": ["string", "null"] } } }, "AdminMaterializeResult": { "type": "object", "required": ["created_registration_count", "moved_registration_count"], "properties": { "created_registration_count": { "type": "integer", "format": "int64" }, "moved_registration_count": { "type": "integer", "format": "int64" } } }, "AdminNotificationEmail": { "type": "object", "description": "One of the two student terminal-state mails, in full: `send_status.failure_code` is what drives\nthe decision to look at the relay.", "required": ["kind", "email_delivery_id", "send_status"], "properties": { "email_delivery_id": { "type": "string", "format": "uuid", "description": "The delivery this registration is pinned to, so support can find the message in the queue and\ntell \"still the first mail\" from \"a second one went out\"." }, "kind": { "$ref": "#/components/schemas/CreditRegistrationNotificationKind" }, "send_status": { "$ref": "#/components/schemas/EmailSendStatusReport" } } }, "AdminPauseCourseModulePayload": { "type": "object", "required": ["reason"], "properties": { "reason": { "type": "string" } } }, "AdminPausePhasePayload": { "type": "object", "required": ["reason"], "properties": { "reason": { "type": "string" } } }, "AdminPhaseActionPayload": { "type": "object", "properties": { "reason": { "type": ["string", "null"] } } }, "AdminRequeueRetryablePayload": { "type": "object", "required": ["reason"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "course_module_id": { "type": ["string", "null"], "format": "uuid" }, "reason": { "type": "string" } } }, "AdminRequeueRetryableResult": { "type": "object", "required": ["requeued_count", "max_rows_per_call"], "properties": { "max_rows_per_call": { "type": "integer", "format": "int64" }, "requeued_count": { "type": "integer", "format": "int64" } } }, "AdminResendAccountLinkingEmailPayload": { "type": "object", "required": ["student_number", "course_id", "override_rate_caps"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "override_rate_caps": { "type": "boolean", "description": "Retires the mails a cap is counting, then runs the ordinary send path. Requires a reason." }, "reason": { "type": ["string", "null"] }, "student_number": { "type": "string" } } }, "AdminResendAccountLinkingEmailResult": { "type": "object", "required": [ "outcome", "retired_mail_count", "linking_emails", "mails_sent_for_this_course", "max_mails_per_person_and_course", "quiet_period_secs" ], "properties": { "linking_emails": { "type": "array", "items": { "$ref": "#/components/schemas/AdminLinkingEmail" } }, "mails_sent_for_this_course": { "type": "integer", "format": "int64" }, "max_mails_per_person_and_course": { "type": "integer", "format": "int64" }, "outcome": { "$ref": "#/components/schemas/AdminResendOutcome" }, "quiet_period_secs": { "type": "integer", "format": "int64" }, "retired_mail_count": { "type": "integer", "format": "int64", "description": "Mails retired to get past a cap. Always zero without an override." } } }, "AdminResendOutcome": { "type": "string", "enum": [ "queued", "already_mailed_to_every_known_address", "refused_by_rate_cap", "no_address_in_study_registry", "not_on_the_course_roster", "already_linked", "study_registry_unavailable" ] }, "AdminResolveStudentNumberPayload": { "type": "object", "required": ["student_number"], "properties": { "student_number": { "type": "string" } } }, "AdminResolveStudentNumberResult": { "type": "object", "description": "The preview a manual link is gated on. No addresses from the registry — `resolve-persons` answers\nwith a name and an id only — so the addresses here are the ones we mailed.", "required": ["found", "student_number", "study_registry_unavailable", "linking_emails"], "properties": { "already_linked_to_user_email": { "type": ["string", "null"] }, "already_linked_to_user_id": { "type": ["string", "null"], "format": "uuid" }, "already_linked_via": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/StudentNumberVerificationMethod" } ] }, "code": { "type": ["string", "null"], "description": "The registry's own per-item code, an identifier rather than prose." }, "first_names": { "type": ["string", "null"] }, "found": { "type": "boolean" }, "last_name": { "type": ["string", "null"] }, "linking_emails": { "type": "array", "items": { "$ref": "#/components/schemas/AdminLinkingEmail" } }, "sisu_person_id": { "type": ["string", "null"], "description": "Echoed back to the manual-link endpoint, which refuses without it." }, "student_number": { "type": "string" }, "study_registry_unavailable": { "type": "boolean" } } }, "AdminResumeCourseModulePayload": { "type": "object", "properties": { "reason": { "type": ["string", "null"] } } }, "AdminSuotarApiCall": { "type": "object", "required": [ "id", "endpoint", "started_at", "succeeded", "request_item_count", "ok_item_count", "error_item_count", "worker_name", "credit_registration_ids" ], "properties": { "credit_registration_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "duration_ms": { "type": ["integer", "null"], "format": "int32" }, "endpoint": { "$ref": "#/components/schemas/SuotarEndpoint" }, "error_item_count": { "type": "integer", "format": "int32" }, "http_status": { "type": ["integer", "null"], "format": "int32" }, "id": { "type": "string", "format": "uuid" }, "ok_item_count": { "type": "integer", "format": "int32" }, "request_body_sample": { "description": "Scrubbed and sampled at write time." }, "request_item_count": { "type": "integer", "format": "int32" }, "request_level_error_code": { "type": ["string", "null"] }, "response_body_sample": {}, "started_at": { "type": "string", "format": "date-time" }, "succeeded": { "type": "boolean" }, "worker_name": { "type": "string" } } }, "AdminTransitionCreditRegistrationPayload": { "type": "object", "required": ["to_state", "reason"], "properties": { "reason": { "type": "string" }, "to_state": { "$ref": "#/components/schemas/AdminCreditRegistrationTransitionTarget" } } }, "AdminTransitionCreditRegistrationResult": { "type": "object", "required": ["outcome", "state", "needs_admin_attention"], "properties": { "needs_admin_attention": { "type": "boolean" }, "outcome": { "$ref": "#/components/schemas/AdminTransitionOutcome" }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" } } }, "AdminTransitionOutcome": { "type": "string", "enum": ["applied", "refused_without_consent", "no_change"] }, "AdminUnlinkStudentNumberPayload": { "type": "object", "required": ["reason"], "properties": { "reason": { "type": "string" } } }, "AdminUnlinkStudentNumberResult": { "type": "object", "required": ["affected_registration_count"], "properties": { "affected_registration_count": { "type": "integer", "format": "int64", "description": "Registrations that went back to waiting for a number." } } }, "AdminVerifiedStudentNumberRow": { "type": "object", "required": [ "id", "user_id", "student_number", "sisu_person_id", "verified_at", "verified_via", "live_registration_count" ], "properties": { "first_name": { "type": ["string", "null"] }, "id": { "type": "string", "format": "uuid" }, "last_name": { "type": ["string", "null"] }, "link_reason": { "type": ["string", "null"] }, "linked_by_user_id": { "type": ["string", "null"], "format": "uuid" }, "live_registration_count": { "type": "integer", "format": "int64" }, "sisu_person_id": { "type": "string" }, "student_number": { "type": "string" }, "user_email": { "type": ["string", "null"], "description": "In full." }, "user_id": { "type": "string", "format": "uuid" }, "verified_at": { "type": "string", "format": "date-time" }, "verified_from_course_id": { "type": ["string", "null"], "format": "uuid" }, "verified_via": { "$ref": "#/components/schemas/StudentNumberVerificationMethod" }, "verified_via_email": { "type": ["string", "null"], "description": "The registry-held address the proof rests on, in full. `None` for an admin-established link." } } }, "AdminVerifiedStudentNumbersPage": { "type": "object", "required": ["data", "total_count", "total_pages"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AdminVerifiedStudentNumberRow" } }, "total_count": { "type": "integer", "format": "int64" }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 } } }, "AnalysisCourseType": { "type": "string", "enum": ["compulsory", "elective"] }, "AnalysisWorkspaceV1": { "type": "object", "description": "Analysis stage form: course metadata, needs, wishes, market, resources, contributors.", "required": [ "mode_synchronous", "mode_asynchronous", "open_period_i", "open_period_ii", "open_period_iii", "open_period_iv", "open_period_all", "wishes_content_format_text", "wishes_content_format_video", "wishes_content_format_podcast", "wishes_content_format_xr" ], "properties": { "contributors_editors": { "type": ["string", "null"] }, "contributors_instructional_designer": { "type": ["string", "null"] }, "contributors_subject_matter_experts": { "type": ["string", "null"] }, "contributors_support_staff": { "type": ["string", "null"] }, "course_title": { "type": ["string", "null"] }, "course_type": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/AnalysisCourseType" } ] }, "credits": { "type": ["number", "null"], "format": "double" }, "degree_programme": { "type": ["string", "null"] }, "language": { "type": ["string", "null"] }, "market_results": { "type": ["string", "null"] }, "mode_asynchronous": { "type": "boolean" }, "mode_synchronous": { "type": "boolean" }, "open_period_all": { "type": "boolean" }, "open_period_i": { "type": "boolean" }, "open_period_ii": { "type": "boolean" }, "open_period_iii": { "type": "boolean" }, "open_period_iv": { "type": "boolean" }, "resources_purchase_budget": { "type": ["string", "null"] }, "resources_university": { "type": ["string", "null"] }, "responsible_teachers": { "type": ["string", "null"] }, "students_demographic_data": { "type": ["string", "null"] }, "target_group": { "type": ["string", "null"] }, "wishes_assessment_text": { "type": ["string", "null"] }, "wishes_content_format_notes": { "type": ["string", "null"] }, "wishes_content_format_podcast": { "type": "boolean" }, "wishes_content_format_text": { "type": "boolean" }, "wishes_content_format_video": { "type": "boolean" }, "wishes_content_format_xr": { "type": "boolean" }, "wishes_other_suggestions": { "type": ["string", "null"] }, "wishes_topics": { "type": ["string", "null"] } } }, "AnswerRequiringAttentionWithTasks": { "type": "object", "required": [ "id", "user_id", "created_at", "updated_at", "grading_progress", "submission_id", "exercise_id", "tasks", "given_peer_reviews", "received_peer_or_self_reviews", "received_peer_review_flagging_reports" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "data_json": {}, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exercise_id": { "type": "string", "format": "uuid" }, "given_peer_reviews": { "type": "array", "items": { "$ref": "#/components/schemas/PeerReviewWithQuestionsAndAnswers" } }, "grading_progress": { "$ref": "#/components/schemas/GradingProgress" }, "id": { "type": "string", "format": "uuid" }, "received_peer_or_self_reviews": { "type": "array", "items": { "$ref": "#/components/schemas/PeerReviewWithQuestionsAndAnswers" } }, "received_peer_review_flagging_reports": { "type": "array", "items": { "$ref": "#/components/schemas/FlaggedAnswer" } }, "score_given": { "type": ["number", "null"], "format": "float" }, "submission_id": { "type": "string", "format": "uuid" }, "tasks": { "type": "array", "items": { "$ref": "#/components/schemas/CourseMaterialExerciseTask" } }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "AnswersRequiringAttention": { "type": "object", "required": ["exercise_max_points", "data", "total_pages"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AnswerRequiringAttentionWithTasks" } }, "exercise_max_points": { "type": "integer", "format": "int32" }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 } } }, "AuthorizedClientInfo": { "type": "object", "required": ["client_id", "client_name", "scopes"], "properties": { "client_id": { "type": "string", "format": "uuid" }, "client_name": { "type": "string" }, "scopes": { "type": "array", "items": { "type": "string" } } } }, "AutomaticCompletionRequirements": { "type": "object", "required": ["course_module_id", "requires_exam"], "properties": { "course_module_id": { "type": "string", "format": "uuid", "description": "Course module associated with these requirements." }, "number_of_exercises_attempted_treshold": { "type": ["integer", "null"], "format": "int32" }, "number_of_points_treshold": { "type": ["integer", "null"], "format": "int32" }, "requires_exam": { "type": "boolean" } } }, "AverageMetric": { "type": "object", "description": "A generic result representing an average metric over a time period.\nThe average value (e.g. average time in seconds) may be absent if no data is available.", "properties": { "average": { "type": ["number", "null"], "format": "double", "description": "The average value. For example, the average time (in seconds) from course start to first submission." }, "period": { "type": ["string", "null"], "format": "date-time", "description": "The start of the time period (e.g., day, week, month) associated with this metric." } } }, "BlockProposal": { "oneOf": [ { "allOf": [ { "$ref": "#/components/schemas/EditedBlockStillExistsData" }, { "type": "object", "required": ["type"], "properties": { "type": { "type": "string", "enum": ["edited-block-still-exists"] } } } ] }, { "allOf": [ { "$ref": "#/components/schemas/EditedBlockNoLongerExistsData" }, { "type": "object", "required": ["type"], "properties": { "type": { "type": "string", "enum": ["edited-block-no-longer-exists"] } } } ] } ] }, "BlockProposalAction": { "oneOf": [ { "type": "object", "required": ["data", "tag"], "properties": { "data": { "type": "string" }, "tag": { "type": "string", "enum": ["Accept"] } } }, { "type": "object", "required": ["tag"], "properties": { "tag": { "type": "string", "enum": ["Reject"] } } } ] }, "BlockProposalInfo": { "type": "object", "required": ["id", "action"], "properties": { "action": { "$ref": "#/components/schemas/BlockProposalAction" }, "id": { "type": "string", "format": "uuid" } } }, "BulkUserDetailsRequest": { "type": "object", "required": ["user_ids", "course_id"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "user_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "CertificateAllRequirements": { "type": "object", "required": ["certificate_configuration_id", "course_module_ids"], "properties": { "certificate_configuration_id": { "type": "string", "format": "uuid" }, "course_module_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "CertificateConfiguration": { "type": "object", "required": [ "id", "created_at", "updated_at", "certificate_owner_name_y_pos", "certificate_owner_name_x_pos", "certificate_owner_name_font_size", "certificate_owner_name_text_color", "certificate_owner_name_text_anchor", "certificate_validate_url_y_pos", "certificate_validate_url_x_pos", "certificate_validate_url_font_size", "certificate_validate_url_text_color", "certificate_validate_url_text_anchor", "certificate_date_y_pos", "certificate_date_x_pos", "certificate_date_font_size", "certificate_date_text_color", "certificate_date_text_anchor", "certificate_locale", "paper_size", "background_svg_path", "background_svg_file_upload_id", "render_certificate_grade" ], "properties": { "background_svg_file_upload_id": { "type": "string", "format": "uuid" }, "background_svg_path": { "type": "string" }, "certificate_date_font_size": { "type": "string" }, "certificate_date_text_anchor": { "$ref": "#/components/schemas/CertificateTextAnchor" }, "certificate_date_text_color": { "type": "string" }, "certificate_date_x_pos": { "type": "string" }, "certificate_date_y_pos": { "type": "string" }, "certificate_grade_font_size": { "type": ["string", "null"] }, "certificate_grade_text_anchor": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CertificateTextAnchor" } ] }, "certificate_grade_text_color": { "type": ["string", "null"] }, "certificate_grade_x_pos": { "type": ["string", "null"] }, "certificate_grade_y_pos": { "type": ["string", "null"] }, "certificate_locale": { "type": "string" }, "certificate_owner_name_font_size": { "type": "string" }, "certificate_owner_name_text_anchor": { "$ref": "#/components/schemas/CertificateTextAnchor" }, "certificate_owner_name_text_color": { "type": "string" }, "certificate_owner_name_x_pos": { "type": "string" }, "certificate_owner_name_y_pos": { "type": "string" }, "certificate_validate_url_font_size": { "type": "string" }, "certificate_validate_url_text_anchor": { "$ref": "#/components/schemas/CertificateTextAnchor" }, "certificate_validate_url_text_color": { "type": "string" }, "certificate_validate_url_x_pos": { "type": "string" }, "certificate_validate_url_y_pos": { "type": "string" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "overlay_svg_file_upload_id": { "type": ["string", "null"], "format": "uuid" }, "overlay_svg_path": { "type": ["string", "null"] }, "paper_size": { "$ref": "#/components/schemas/PaperSize" }, "render_certificate_grade": { "type": "boolean" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CertificateConfigurationAndRequirements": { "type": "object", "required": ["certificate_configuration", "requirements"], "properties": { "certificate_configuration": { "$ref": "#/components/schemas/CertificateConfiguration" }, "requirements": { "$ref": "#/components/schemas/CertificateAllRequirements" } } }, "CertificateConfigurationUpdate": { "type": "object", "required": ["course_module_id", "clear_overlay_svg_file", "render_certificate_grade"], "properties": { "background_svg_file_name": { "type": ["string", "null"] }, "certificate_date_font_size": { "type": ["string", "null"] }, "certificate_date_text_anchor": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CertificateTextAnchor" } ] }, "certificate_date_text_color": { "type": ["string", "null"] }, "certificate_date_x_pos": { "type": ["string", "null"] }, "certificate_date_y_pos": { "type": ["string", "null"] }, "certificate_grade_font_size": { "type": ["string", "null"] }, "certificate_grade_text_anchor": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CertificateTextAnchor" } ] }, "certificate_grade_text_color": { "type": ["string", "null"] }, "certificate_grade_x_pos": { "type": ["string", "null"] }, "certificate_grade_y_pos": { "type": ["string", "null"] }, "certificate_locale": { "type": ["string", "null"] }, "certificate_owner_name_font_size": { "type": ["string", "null"] }, "certificate_owner_name_text_anchor": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CertificateTextAnchor" } ] }, "certificate_owner_name_text_color": { "type": ["string", "null"] }, "certificate_owner_name_x_pos": { "type": ["string", "null"] }, "certificate_owner_name_y_pos": { "type": ["string", "null"] }, "certificate_validate_url_font_size": { "type": ["string", "null"] }, "certificate_validate_url_text_anchor": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CertificateTextAnchor" } ] }, "certificate_validate_url_text_color": { "type": ["string", "null"] }, "certificate_validate_url_x_pos": { "type": ["string", "null"] }, "certificate_validate_url_y_pos": { "type": ["string", "null"] }, "clear_overlay_svg_file": { "type": "boolean" }, "course_instance_id": { "type": ["string", "null"], "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "overlay_svg_file_name": { "type": ["string", "null"] }, "paper_size": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/PaperSize" } ] }, "render_certificate_grade": { "type": "boolean" } } }, "CertificateGenerationRequest": { "type": "object", "required": ["certificate_configuration_id", "name_on_certificate"], "properties": { "certificate_configuration_id": { "type": "string", "format": "uuid" }, "grade": { "type": ["string", "null"] }, "name_on_certificate": { "type": "string" } } }, "CertificateGridRow": { "type": "object", "required": ["user_id"], "properties": { "certificate_id": { "type": ["string", "null"], "format": "uuid" }, "date_issued": { "type": ["string", "null"], "format": "date-time" }, "name_on_certificate": { "type": ["string", "null"] }, "user_id": { "type": "string", "format": "uuid" }, "verification_id": { "type": ["string", "null"] } } }, "CertificateTextAnchor": { "type": "string", "description": "How text should be positioned relative to the given coordinates. See .", "enum": ["start", "middle", "end"] }, "CertificateUpdateRequest": { "type": "object", "required": ["date_issued"], "properties": { "date_issued": { "type": "string", "format": "date-time" }, "name_on_certificate": { "type": ["string", "null"] } } }, "ChangePasswordData": { "type": "object", "required": ["old_password", "new_password"], "properties": { "new_password": { "type": "string" }, "old_password": { "type": "string" } } }, "Chapter": { "type": "object", "required": [ "id", "created_at", "updated_at", "name", "course_id", "chapter_number", "course_module_id" ], "properties": { "chapter_image_url": { "type": ["string", "null"] }, "chapter_number": { "type": "integer", "format": "int32" }, "color": { "type": ["string", "null"] }, "copied_from": { "type": ["string", "null"], "format": "uuid" }, "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deadline": { "type": ["string", "null"], "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "front_page_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "opens_at": { "type": ["string", "null"], "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" } } }, "ChapterAvailability": { "type": "object", "required": [ "chapter_id", "chapter_number", "chapter_name", "exercises_available", "points_available" ], "properties": { "chapter_id": { "type": "string", "format": "uuid" }, "chapter_name": { "type": "string" }, "chapter_number": { "type": "integer", "format": "int32" }, "exercises_available": { "type": "integer", "format": "int64" }, "points_available": { "type": "integer", "format": "int64" } } }, "ChapterLockStatusActionPayload": { "type": "object", "required": ["status"], "properties": { "status": { "$ref": "#/components/schemas/ChapterLockingStatus" } } }, "ChapterLockingStatus": { "type": "string", "enum": ["unlocked", "completed_and_locked", "not_unlocked_yet"] }, "ChapterScore": { "allOf": [ { "$ref": "#/components/schemas/DatabaseChapter" }, { "type": "object", "required": ["score_given", "score_total"], "properties": { "score_given": { "type": "number", "format": "float" }, "score_total": { "type": "integer", "format": "int32" } } } ] }, "ChapterUpdate": { "type": "object", "required": ["name"], "properties": { "color": { "type": ["string", "null"] }, "course_module_id": { "type": ["string", "null"], "format": "uuid", "description": "CHANGE TO NON NULL WHEN FRONTEND MODULE EDITING IMPLEMENTED" }, "deadline": { "type": ["string", "null"], "format": "date-time" }, "front_page_id": { "type": ["string", "null"], "format": "uuid" }, "name": { "type": "string" }, "opens_at": { "type": ["string", "null"], "format": "date-time" } } }, "ChatbotConfiguration": { "type": "object", "required": [ "id", "created_at", "updated_at", "enabled_to_students", "chatbot_name", "model_id", "prompt", "initial_message", "weekly_tokens_per_user", "daily_tokens_per_user", "temperature", "top_p", "frequency_penalty", "presence_penalty", "max_output_tokens", "verbosity", "reasoning_effort", "use_azure_search", "maintain_azure_search_index", "hide_citations", "use_semantic_reranking", "enabled_tool_categories", "default_chatbot", "suggest_next_messages", "publicly_accessible" ], "properties": { "chatbot_name": { "type": "string" }, "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "daily_tokens_per_user": { "type": "integer", "format": "int32" }, "default_chatbot": { "type": "boolean" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "enabled_to_students": { "type": "boolean" }, "enabled_tool_categories": { "type": "array", "items": { "$ref": "#/components/schemas/ToolCategory" } }, "frequency_penalty": { "type": "number", "format": "float" }, "hide_citations": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "initial_message": { "type": "string" }, "initial_suggested_messages": { "type": ["array", "null"], "items": { "type": "string" } }, "maintain_azure_search_index": { "type": "boolean" }, "max_output_tokens": { "type": "integer", "format": "int32" }, "model_id": { "type": "string", "format": "uuid" }, "presence_penalty": { "type": "number", "format": "float" }, "prompt": { "type": "string" }, "publicly_accessible": { "type": "boolean" }, "reasoning_effort": { "$ref": "#/components/schemas/ReasoningEffortLevel" }, "suggest_next_messages": { "type": "boolean" }, "temperature": { "type": "number", "format": "float" }, "top_p": { "type": "number", "format": "float" }, "updated_at": { "type": "string", "format": "date-time" }, "use_azure_search": { "type": "boolean" }, "use_semantic_reranking": { "type": "boolean" }, "verbosity": { "$ref": "#/components/schemas/VerbosityLevel" }, "weekly_tokens_per_user": { "type": "integer", "format": "int32" } } }, "ChatbotConfigurationModel": { "type": "object", "required": [ "id", "created_at", "updated_at", "model", "model_type", "default_model", "context_size" ], "properties": { "context_size": { "type": "integer", "format": "int32" }, "created_at": { "type": "string", "format": "date-time" }, "default_model": { "type": "boolean" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "model": { "type": "string" }, "model_type": { "$ref": "#/components/schemas/ModelType" }, "updated_at": { "type": "string", "format": "date-time" } } }, "ClaimStudentNumberVerificationTokenOutcome": { "type": "string", "enum": [ "linked", "already_linked_to_this_account", "expired", "already_used", "student_number_already_linked_to_another_account" ] }, "ClaimStudentNumberVerificationTokenResult": { "type": "object", "required": ["outcome", "newly_unblocked_registration_count"], "properties": { "linked_course_name": { "type": ["string", "null"] }, "newly_unblocked_registration_count": { "type": "integer", "format": "int64", "description": "Completions that stopped waiting for a student number because of this claim." }, "outcome": { "$ref": "#/components/schemas/ClaimStudentNumberVerificationTokenOutcome" }, "student_number": { "type": ["string", "null"] } } }, "CmsPageExercise": { "type": "object", "required": [ "id", "name", "order_number", "score_maximum", "limit_number_of_tries", "needs_peer_review", "needs_self_review", "use_course_default_peer_or_self_review_config", "teacher_reviews_answer_after_locking" ], "properties": { "deadline": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "limit_number_of_tries": { "type": "boolean" }, "max_tries_per_slide": { "type": ["integer", "null"], "format": "int32" }, "name": { "type": "string" }, "needs_peer_review": { "type": "boolean" }, "needs_self_review": { "type": "boolean" }, "order_number": { "type": "integer", "format": "int32" }, "peer_or_self_review_config": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CmsPeerOrSelfReviewConfig" } ] }, "peer_or_self_review_questions": { "type": ["array", "null"], "items": { "$ref": "#/components/schemas/CmsPeerOrSelfReviewQuestion" } }, "score_maximum": { "type": "integer", "format": "int32" }, "teacher_reviews_answer_after_locking": { "type": "boolean" }, "use_course_default_peer_or_self_review_config": { "type": "boolean" } } }, "CmsPageExerciseSlide": { "type": "object", "required": ["id", "exercise_id", "order_number"], "properties": { "exercise_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "order_number": { "type": "integer", "format": "int32" } } }, "CmsPageExerciseTask": { "type": "object", "required": ["id", "exercise_slide_id", "assignment", "exercise_type", "order_number"], "properties": { "assignment": {}, "exercise_slide_id": { "type": "string", "format": "uuid" }, "exercise_type": { "type": "string" }, "id": { "type": "string", "format": "uuid" }, "order_number": { "type": "integer", "format": "int32" }, "private_spec": {} } }, "CmsPeerOrSelfReviewConfig": { "type": "object", "required": [ "id", "course_id", "peer_reviews_to_give", "peer_reviews_to_receive", "accepting_threshold", "processing_strategy", "points_are_all_or_nothing", "reset_answer_if_zero_points_from_review" ], "properties": { "accepting_threshold": { "type": "number", "format": "float" }, "course_id": { "type": "string", "format": "uuid" }, "exercise_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "peer_reviews_to_give": { "type": "integer", "format": "int32" }, "peer_reviews_to_receive": { "type": "integer", "format": "int32" }, "points_are_all_or_nothing": { "type": "boolean" }, "processing_strategy": { "$ref": "#/components/schemas/PeerReviewProcessingStrategy" }, "reset_answer_if_zero_points_from_review": { "type": "boolean" }, "review_instructions": {} } }, "CmsPeerOrSelfReviewQuestion": { "type": "object", "required": [ "id", "peer_or_self_review_config_id", "order_number", "question", "question_type", "answer_required", "weight" ], "properties": { "answer_required": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "order_number": { "type": "integer", "format": "int32" }, "peer_or_self_review_config_id": { "type": "string", "format": "uuid" }, "question": { "type": "string" }, "question_type": { "$ref": "#/components/schemas/PeerOrSelfReviewQuestionType" }, "weight": { "type": "number", "format": "float" } } }, "CodeGiveaway": { "type": "object", "required": ["id", "created_at", "updated_at", "course_id", "enabled", "name"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "enabled": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "require_course_specific_consent_form_question_id": { "type": ["string", "null"], "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CodeGiveawayCode": { "type": "object", "required": [ "id", "created_at", "updated_at", "code_giveaway_id", "added_by_user_id", "code" ], "properties": { "added_by_user_id": { "type": "string", "format": "uuid" }, "code": { "type": "string" }, "code_giveaway_id": { "type": "string", "format": "uuid" }, "code_given_to_user_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CohortActivity": { "type": "object", "description": "Represents cohort activity metrics for both weekly and daily cohorts.\nFor daily cohorts, `offset` will be populated (and `activity_period` may be computed from it);\nfor weekly cohorts, `offset` will be `None` and `activity_period` indicates the week start.", "required": ["active_users"], "properties": { "active_users": { "type": "integer", "format": "int64", "description": "The number of active users in this cohort for the given period." }, "activity_period": { "type": ["string", "null"], "format": "date-time", "description": "The activity period (for example, the start of the week or the computed activity day)." }, "cohort_start": { "type": ["string", "null"], "format": "date-time", "description": "The start date of the cohort (either day or week)." }, "offset": { "type": ["integer", "null"], "format": "int32", "description": "The day offset from the cohort start (only applicable for daily cohorts)." } } }, "CompleteCourseMetadata": { "type": "object", "required": [ "course", "course_instances", "default_module", "course_prerequisites", "course_audiences", "course_organization" ], "properties": { "course": { "$ref": "#/components/schemas/Course" }, "course_audiences": { "type": "array", "items": { "$ref": "#/components/schemas/CourseAudience" } }, "course_instances": { "type": "array", "items": { "$ref": "#/components/schemas/CourseInstance" } }, "course_organization": { "$ref": "#/components/schemas/DatabaseOrganization" }, "course_prerequisites": { "type": "array", "items": { "$ref": "#/components/schemas/CoursePrerequisite" } }, "default_module": { "$ref": "#/components/schemas/CourseModule" } } }, "CompletionGridRow": { "type": "object", "required": ["user_id", "module_id", "registered", "needs_to_be_reviewed"], "properties": { "grade": { "type": ["integer", "null"], "format": "int32" }, "module": { "type": ["string", "null"] }, "module_id": { "type": "string", "format": "uuid" }, "needs_to_be_reviewed": { "type": "boolean" }, "passed": { "type": ["boolean", "null"] }, "registered": { "type": "boolean" }, "user_id": { "type": "string", "format": "uuid" } } }, "CompletionPolicy": { "oneOf": [ { "allOf": [ { "$ref": "#/components/schemas/AutomaticCompletionRequirements" }, { "type": "object", "required": ["policy"], "properties": { "policy": { "type": "string", "enum": ["automatic"] } } } ] }, { "type": "object", "required": ["policy"], "properties": { "policy": { "type": "string", "enum": ["manual"] } } } ] }, "CompletionRegistrationLink": { "type": "object", "required": ["url"], "properties": { "url": { "type": "string" } } }, "ConsentData": { "type": "object", "required": ["consent"], "properties": { "consent": { "type": "boolean" } } }, "ConsentDenyQuery": { "type": "object", "required": ["client_id", "redirect_uri", "state"], "properties": { "client_id": { "type": "string" }, "redirect_uri": { "type": "string" }, "state": { "type": "string" } } }, "ConsentQuery": { "type": "object", "required": ["client_id", "redirect_uri", "response_type", "scope", "state", "nonce"], "properties": { "client_id": { "type": "string" }, "code_challenge": { "type": ["string", "null"] }, "code_challenge_method": { "type": ["string", "null"] }, "nonce": { "type": "string" }, "redirect_uri": { "type": "string" }, "response_type": { "type": "string" }, "scope": { "type": "string" }, "state": { "type": "string" } } }, "ConsentResponse": { "type": "object", "required": ["redirect_uri"], "properties": { "redirect_uri": { "type": "string" } } }, "CopyCourseMode": { "oneOf": [ { "type": "object", "description": "Create a completely separate copy with a new course language group", "required": ["mode"], "properties": { "mode": { "type": "string", "enum": ["duplicate"] } } }, { "type": "object", "description": "Create a new language version within the same language group as the source", "required": ["mode"], "properties": { "mode": { "type": "string", "enum": ["same_language_group"] } } }, { "type": "object", "description": "Create a new language version in a specified language group", "required": ["target_course_id", "mode"], "properties": { "mode": { "type": "string", "enum": ["existing_language_group"] }, "target_course_id": { "type": "string", "format": "uuid" } } }, { "type": "object", "description": "Create a new language version in a new language group", "required": ["mode"], "properties": { "mode": { "type": "string", "enum": ["new_language_group"] } } } ] }, "CopyCourseRequest": { "allOf": [ { "$ref": "#/components/schemas/NewCourse" }, { "type": "object", "required": ["mode"], "properties": { "mode": { "$ref": "#/components/schemas/CopyCourseMode" } } } ] }, "CountResult": { "type": "object", "description": "A generic result representing a count metric over a time period.\nWhen the time period is not applicable (for overall totals), `period` will be `None`.", "required": ["count"], "properties": { "count": { "type": "integer", "format": "int64", "description": "The count (for example, the number of users)." }, "period": { "type": ["string", "null"], "format": "date-time", "description": "The start of the time period (e.g., day, week, month) associated with this count.\nFor overall totals, this will be `None`." } } }, "Course": { "type": "object", "required": [ "id", "slug", "created_at", "updated_at", "name", "organization_id", "language_code", "course_language_group_id", "is_draft", "is_test_mode", "is_unlisted", "base_module_completion_requires_n_submodule_completions", "can_add_chatbot", "is_joinable_by_code_only", "ask_marketing_consent", "flagged_answers_skip_manual_review_and_allow_retry", "chapter_locking_enabled", "cheater_detection_enabled", "ai_policy" ], "properties": { "ai_policy": { "$ref": "#/components/schemas/CourseAiPolicy" }, "ask_marketing_consent": { "type": "boolean" }, "base_module_completion_requires_n_submodule_completions": { "type": "integer", "format": "int32" }, "can_add_chatbot": { "type": "boolean" }, "chapter_locking_enabled": { "type": "boolean" }, "cheater_detection_enabled": { "type": "boolean" }, "closed_additional_message": { "type": ["string", "null"] }, "closed_at": { "type": ["string", "null"], "format": "date-time" }, "closed_course_successor_id": { "type": ["string", "null"], "format": "uuid" }, "content_search_language": { "type": ["string", "null"] }, "copied_from": { "type": ["string", "null"], "format": "uuid" }, "course_language_group_id": { "type": "string", "format": "uuid" }, "course_material_ai_instructions": { "type": ["boolean", "null"] }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "description": { "type": ["string", "null"] }, "flagged_answers_skip_manual_review_and_allow_retry": { "type": "boolean" }, "flagged_answers_threshold": { "type": ["integer", "null"], "format": "int32" }, "id": { "type": "string", "format": "uuid" }, "is_draft": { "type": "boolean" }, "is_joinable_by_code_only": { "type": "boolean" }, "is_test_mode": { "type": "boolean" }, "is_unlisted": { "type": "boolean" }, "join_code": { "type": ["string", "null"] }, "language_code": { "type": "string" }, "name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" }, "slug": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseAiPolicy": { "type": "string", "description": "The AI policy a teacher has selected for a course. Drives which variant of the student-facing\nAI usage notice is shown; `NotSet` (the default) keeps the generic default message.", "enum": ["NotSet", "NoAi", "PlanningOnly", "Limited", "FullUse", "Required"] }, "CourseAudience": { "type": "object", "required": ["id", "created_at", "updated_at", "course_id", "audience"], "properties": { "audience": { "type": "string" }, "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "embedding": { "type": ["array", "null"], "items": { "type": "number", "format": "float" } }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseAuditingData": { "type": "object", "required": [ "id", "name", "slug", "created_at", "updated_at", "organization_id", "organization_name", "organization_slug", "prerequisites", "audiences", "modules" ], "properties": { "audiences": { "type": "array", "items": { "$ref": "#/components/schemas/EditCourseAudience" } }, "closed_additional_message": { "type": ["string", "null"] }, "closed_at": { "type": ["string", "null"], "format": "date-time" }, "closed_course_successor_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "description": { "type": ["string", "null"] }, "id": { "type": "string", "format": "uuid" }, "modules": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModule" } }, "name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" }, "organization_name": { "type": "string" }, "organization_slug": { "type": "string" }, "prerequisites": { "type": "array", "items": { "$ref": "#/components/schemas/EditCoursePrerequisite" } }, "slug": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseAuditingDataUpdate": { "type": "object", "required": ["prerequisites", "audiences", "modules"], "properties": { "audiences": { "type": "array", "items": { "$ref": "#/components/schemas/EditCourseAudience" } }, "closed_additional_message": { "type": ["string", "null"] }, "closed_at": { "type": ["string", "null"], "format": "date-time" }, "closed_course_successor_id": { "type": ["string", "null"], "format": "uuid" }, "description": { "type": ["string", "null"] }, "modules": { "type": "array", "items": { "$ref": "#/components/schemas/CourseAuditingModuleUpdate" } }, "prerequisites": { "type": "array", "items": { "$ref": "#/components/schemas/EditCoursePrerequisite" } } } }, "CourseAuditingModuleUpdate": { "type": "object", "required": ["id", "order_number", "enable_registering_completion_to_uh_open_university"], "properties": { "completion_registration_link_override": { "type": ["string", "null"] }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "enable_registering_completion_to_uh_open_university": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": ["string", "null"] }, "order_number": { "type": "integer", "format": "int32" }, "uh_course_code": { "type": ["string", "null"] } } }, "CourseBreadcrumbInfo": { "type": "object", "required": [ "course_id", "course_name", "course_slug", "organization_slug", "organization_name" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "course_slug": { "type": "string" }, "organization_name": { "type": "string" }, "organization_slug": { "type": "string" } } }, "CourseCompletionStats": { "type": "object", "required": [ "course_id", "course_name", "total_completions", "unique_users", "registered_completions", "not_registered_completions", "users_with_some_registered_completions", "users_with_some_unregistered_completions", "registered_ects_credits", "not_registered_ects_credits" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "not_registered_completions": { "type": "integer", "format": "int64" }, "not_registered_ects_credits": { "type": "number", "format": "float" }, "registered_completion_percentage": { "type": ["number", "null"], "format": "double" }, "registered_completions": { "type": "integer", "format": "int64" }, "registered_ects_credits": { "type": "number", "format": "float" }, "total_completions": { "type": "integer", "format": "int64" }, "unique_users": { "type": "integer", "format": "int64" }, "users_with_some_registered_completions": { "type": "integer", "format": "int64" }, "users_with_some_unregistered_completions": { "type": "integer", "format": "int64" } } }, "CourseCount": { "type": "object", "required": ["count"], "properties": { "count": { "type": "integer", "format": "int32", "minimum": 0 } } }, "CourseCreditRegistration": { "type": "object", "required": [ "id", "user_id", "course_id", "course_module_id", "course_instance_id", "course_module_completion_id", "completion_date", "state", "state_entered_at", "student_facing_status", "needs_admin_attention", "next_attempt_at", "attempt_number", "superseded" ], "properties": { "attempt_number": { "type": "integer", "format": "int32" }, "completion_date": { "type": "string", "format": "date-time" }, "course_id": { "type": "string", "format": "uuid" }, "course_instance_id": { "type": "string", "format": "uuid" }, "course_module_completion_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "credits": { "type": ["number", "null"], "format": "float" }, "email": { "type": ["string", "null"] }, "enrolment_realisation_name": { "type": ["string", "null"] }, "error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "first_name": { "type": ["string", "null"] }, "grade_id": { "type": ["string", "null"] }, "id": { "type": "string", "format": "uuid" }, "last_name": { "type": ["string", "null"] }, "linking_email": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TeacherLinkingEmailStatus", "description": "Only where we can join the account to a Sisu person, which needs a link past or present." } ] }, "needs_admin_attention": { "type": "boolean" }, "next_attempt_at": { "type": "string", "format": "date-time" }, "notification_email": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/NotificationEmailStatus", "description": "The terminal-state mail this row's status has, if one has been queued. Same derivation the\nstudent sees, so a teacher answering \"did they hear from you\" cannot be told something else." } ] }, "registered_at": { "type": ["string", "null"], "format": "date-time" }, "sisu_attainment_id": { "type": ["string", "null"] }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" }, "state_entered_at": { "type": "string", "format": "date-time" }, "student_facing_status": { "$ref": "#/components/schemas/StudentFacingCreditRegistrationStatus", "description": "The same collapsed stage the student is shown, so both audiences read one classification." }, "student_number": { "type": ["string", "null"], "description": "In full: a masked number cannot be checked against a student card." }, "student_number_verified_at": { "type": ["string", "null"], "format": "date-time" }, "student_number_verified_via": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/StudentNumberVerificationMethod", "description": "`admin_manual` means support established the link rather than the student proving it." } ] }, "superseded": { "type": "boolean" }, "user_id": { "type": "string", "format": "uuid" } } }, "CourseCreditRegistrationAction": { "type": "object", "description": "One audited manual action on this course, named rather than keyed: a teacher reads this to find\nout whether a colleague has already acted.", "required": ["id", "created_at", "action", "target_kind", "actor_user_id", "actor_role"], "properties": { "action": { "$ref": "#/components/schemas/CreditRegistrationAdminAction" }, "actor_first_name": { "type": ["string", "null"] }, "actor_last_name": { "type": ["string", "null"] }, "actor_role": { "type": "string", "description": "`global_admin` or `course_teacher`; support acting on the course looks different from a\ncolleague acting on it." }, "actor_user_id": { "type": "string", "format": "uuid" }, "affected_row_count": { "type": ["integer", "null"], "format": "int32" }, "after_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "before_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "created_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "reason": { "type": ["string", "null"] }, "target_id": { "type": ["string", "null"], "format": "uuid" }, "target_kind": { "$ref": "#/components/schemas/CreditRegistrationAdminActionTarget" } } }, "CourseCreditRegistrationBlockedStudentCounts": { "type": "object", "description": "Why the course's enrolled students are not going to get credits. Neither count includes a student\nwho has both consented and linked a number.", "required": ["unlinked_consented_student_count", "no_consent_student_count"], "properties": { "no_consent_student_count": { "type": "integer", "format": "int64", "description": "Never asked or declined." }, "unlinked_consented_student_count": { "type": "integer", "format": "int64", "description": "Consented, but we hold no student number for them." } } }, "CourseCreditRegistrationEvent": { "type": "object", "description": "One event of the item timeline, without the stored request and response bodies: those are the\nadmin dashboard's, and the study registry's own wording is never rendered.", "required": ["id", "created_at", "kind"], "properties": { "actor_user_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "from_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "id": { "type": "string", "format": "uuid" }, "kind": { "$ref": "#/components/schemas/CreditRegistrationEventKind" }, "message": { "type": ["string", "null"], "description": "Our own wording, written by the pipeline or by whoever acted." }, "to_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] } } }, "CourseCreditRegistrationModuleConfigs": { "type": "object", "description": "Every module of the course with its Suotar configuration, for the module editor.", "required": ["modules", "realisations"], "properties": { "modules": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModuleCreditRegistrationConfig" } }, "realisations": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModuleSuotarRealisation" }, "description": "Every live realisation of every module of the course, to be grouped by `course_module_id`." } } }, "CourseCreditRegistrationModuleSummary": { "type": "object", "required": [ "course_module_id", "enabled", "paused", "counts_by_state", "success_count", "failed_permanent_count", "needs_admin_attention_count" ], "properties": { "counts_by_state": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationStateCount" } }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "enabled": { "type": "boolean" }, "failed_permanent_count": { "type": "integer", "format": "int64", "description": "`failed_permanent` only: a retrying row is still working and `misregistered` is not terminal." }, "needs_admin_attention_count": { "type": "integer", "format": "int64" }, "paused": { "type": "boolean" }, "success_count": { "type": "integer", "format": "int64", "description": "`registered`, `duplicate` and `not_improved`: the credit exists in Sisu." } } }, "CourseCreditRegistrationSummary": { "type": "object", "required": ["modules", "blocked_students", "linking_emails_failed_to_send_count"], "properties": { "blocked_students": { "$ref": "#/components/schemas/CourseCreditRegistrationBlockedStudentCounts" }, "linking_emails_failed_to_send_count": { "type": "integer", "format": "int64", "description": "Of the unlinked consented students, the ones whose linking mail we never managed to hand over." }, "modules": { "type": "array", "items": { "$ref": "#/components/schemas/CourseCreditRegistrationModuleSummary" } } } }, "CourseCreditRegistrationUserIdsPayload": { "type": "object", "description": "Body for the students-tab batch: the users of the current identity-list page.", "required": ["user_ids"], "properties": { "course_instance_id": { "type": ["string", "null"], "format": "uuid" }, "user_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "CourseCreditRegistrationsPage": { "type": "object", "required": ["data", "total_pages"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/CourseCreditRegistration" } }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 } } }, "CourseDesignerCourseSize": { "type": "string", "enum": ["small", "medium", "large"] }, "CourseDesignerPlan": { "type": "object", "required": ["id", "created_at", "updated_at", "created_by_user_id", "status"], "properties": { "active_stage": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CourseDesignerStage" } ] }, "created_at": { "type": "string", "format": "date-time" }, "created_by_user_id": { "type": "string", "format": "uuid" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "last_weekly_stage_email_sent_at": { "type": ["string", "null"], "format": "date-time" }, "name": { "type": ["string", "null"] }, "status": { "$ref": "#/components/schemas/CourseDesignerPlanStatus" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseDesignerPlanDetails": { "type": "object", "required": ["plan", "members", "stages"], "properties": { "members": { "type": "array", "items": { "$ref": "#/components/schemas/CourseDesignerPlanMember" } }, "plan": { "$ref": "#/components/schemas/CourseDesignerPlan" }, "stages": { "type": "array", "items": { "$ref": "#/components/schemas/CourseDesignerPlanStageWithTasks" } } } }, "CourseDesignerPlanMember": { "type": "object", "required": ["id", "created_at", "updated_at", "user_id"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "CourseDesignerPlanStage": { "type": "object", "required": [ "id", "created_at", "updated_at", "stage", "status", "planned_starts_on", "planned_ends_on" ], "properties": { "actual_completed_at": { "type": ["string", "null"], "format": "date-time" }, "actual_started_at": { "type": ["string", "null"], "format": "date-time" }, "created_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "planned_ends_on": { "type": "string", "format": "date" }, "planned_starts_on": { "type": "string", "format": "date" }, "stage": { "$ref": "#/components/schemas/CourseDesignerStage" }, "status": { "$ref": "#/components/schemas/CourseDesignerPlanStageStatus" }, "updated_at": { "type": "string", "format": "date-time" }, "workspace_data": {} } }, "CourseDesignerPlanStageStatus": { "type": "string", "enum": ["NotStarted", "InProgress", "Completed"] }, "CourseDesignerPlanStageTask": { "type": "object", "required": [ "id", "created_at", "updated_at", "course_designer_plan_stage_id", "title", "order_number", "is_completed", "is_auto_generated" ], "properties": { "completed_at": { "type": ["string", "null"], "format": "date-time" }, "completed_by_user_id": { "type": ["string", "null"], "format": "uuid" }, "course_designer_plan_stage_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "created_by_user_id": { "type": ["string", "null"], "format": "uuid" }, "description": { "type": ["string", "null"] }, "id": { "type": "string", "format": "uuid" }, "is_auto_generated": { "type": "boolean" }, "is_completed": { "type": "boolean" }, "order_number": { "type": "integer", "format": "int32" }, "title": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseDesignerPlanStageWithTasks": { "allOf": [ { "$ref": "#/components/schemas/CourseDesignerPlanStage" }, { "type": "object", "required": ["tasks"], "properties": { "tasks": { "type": "array", "items": { "$ref": "#/components/schemas/CourseDesignerPlanStageTask" } } } } ] }, "CourseDesignerPlanStatus": { "type": "string", "enum": ["Draft", "Scheduling", "InProgress", "Completed", "Archived"] }, "CourseDesignerPlanSummary": { "type": "object", "required": [ "id", "created_at", "updated_at", "created_by_user_id", "status", "member_count", "stage_count" ], "properties": { "active_stage": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CourseDesignerStage" } ] }, "created_at": { "type": "string", "format": "date-time" }, "created_by_user_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "last_weekly_stage_email_sent_at": { "type": ["string", "null"], "format": "date-time" }, "member_count": { "type": "integer", "format": "int64" }, "name": { "type": ["string", "null"] }, "stage_count": { "type": "integer", "format": "int64" }, "status": { "$ref": "#/components/schemas/CourseDesignerPlanStatus" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseDesignerScheduleStageInput": { "type": "object", "required": ["stage", "planned_starts_on", "planned_ends_on"], "properties": { "planned_ends_on": { "type": "string", "format": "date" }, "planned_starts_on": { "type": "string", "format": "date" }, "stage": { "$ref": "#/components/schemas/CourseDesignerStage" } } }, "CourseDesignerScheduleSuggestionRequest": { "type": "object", "required": ["course_size", "starts_on"], "properties": { "course_size": { "$ref": "#/components/schemas/CourseDesignerCourseSize" }, "starts_on": { "type": "string", "format": "date" } } }, "CourseDesignerScheduleSuggestionResponse": { "type": "object", "required": ["stages"], "properties": { "stages": { "type": "array", "items": { "$ref": "#/components/schemas/CourseDesignerScheduleStageInput" } } } }, "CourseDesignerStage": { "type": "string", "enum": ["Analysis", "Design", "Development", "Implementation", "Evaluation"] }, "CourseDesignerStageWorkspace": { "oneOf": [ { "type": "object", "required": ["payload", "schema"], "properties": { "payload": { "$ref": "#/components/schemas/AnalysisWorkspaceV1" }, "schema": { "type": "string", "enum": ["analysis_v1"] } } } ], "description": "Discriminant for forward-compatible workspace payloads stored in `workspace_data`." }, "CourseEnrollmentInfo": { "type": "object", "required": [ "course_id", "course", "course_instances", "course_modules", "course_module_completions", "course_module_completions_needing_review", "first_enrolled_at", "is_current" ], "properties": { "course": { "$ref": "#/components/schemas/Course" }, "course_id": { "type": "string", "format": "uuid" }, "course_instances": { "type": "array", "items": { "$ref": "#/components/schemas/CourseInstance" } }, "course_module_completions": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModuleCompletion" } }, "course_module_completions_needing_review": { "type": "integer", "format": "int32" }, "course_modules": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModuleInfo" }, "description": "All non-deleted modules of the course, ordered by `order_number`." }, "first_enrolled_at": { "type": "string", "format": "date-time" }, "is_current": { "type": "boolean" }, "user_course_settings": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/UserCourseSettings" } ] } } }, "CourseEnrollmentsInfo": { "type": "object", "required": ["course_enrollments"], "properties": { "course_enrollments": { "type": "array", "items": { "$ref": "#/components/schemas/CourseEnrollmentInfo" } } } }, "CourseExam": { "type": "object", "required": ["id", "course_id", "course_name", "name"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" } } }, "CourseInstance": { "type": "object", "required": [ "id", "created_at", "updated_at", "course_id", "teacher_in_charge_name", "teacher_in_charge_email" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "description": { "type": ["string", "null"] }, "ends_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": ["string", "null"] }, "starts_at": { "type": ["string", "null"], "format": "date-time" }, "support_email": { "type": ["string", "null"] }, "teacher_in_charge_email": { "type": "string" }, "teacher_in_charge_name": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseInstanceCompletionSummary": { "type": "object", "required": ["course_modules", "users_with_course_module_completions"], "properties": { "course_modules": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModule" } }, "users_with_course_module_completions": { "type": "array", "items": { "$ref": "#/components/schemas/UserWithModuleCompletions" } } } }, "CourseInstanceForm": { "type": "object", "required": ["teacher_in_charge_name", "teacher_in_charge_email"], "properties": { "closing_time": { "type": ["string", "null"], "format": "date-time" }, "description": { "type": ["string", "null"] }, "name": { "type": ["string", "null"] }, "opening_time": { "type": ["string", "null"], "format": "date-time" }, "support_email": { "type": ["string", "null"] }, "teacher_in_charge_email": { "type": "string" }, "teacher_in_charge_name": { "type": "string" } } }, "CourseMaterialExerciseTask": { "type": "object", "description": "Information necessary for the frontend to render an exercise task", "required": [ "id", "exercise_service_slug", "exercise_slide_id", "assignment", "order_number" ], "properties": { "assignment": {}, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exercise_iframe_url": { "type": ["string", "null"], "description": "\n If none, the task is not completable at the moment because the service needs to\n be configured to the system." }, "exercise_service_slug": { "type": "string" }, "exercise_slide_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "model_solution_spec": {}, "order_number": { "type": "integer", "format": "int32" }, "previous_submission": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ExerciseTaskSubmission" } ] }, "previous_submission_grading": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ExerciseTaskGrading" } ] }, "pseudonumous_user_id": { "type": ["string", "null"], "format": "uuid", "description": "\n Unique for each (exercise_service, user) combo. If none, the task is not completable at the moment because the service needs to\n be configured to the system." }, "public_spec": {} } }, "CourseMetadata": { "type": "object", "required": ["course_audiences", "course_prerequisites", "course_updated_at"], "properties": { "course_audiences": { "type": "array", "items": { "$ref": "#/components/schemas/CourseAudience" } }, "course_description": { "type": ["string", "null"] }, "course_prerequisites": { "type": "array", "items": { "$ref": "#/components/schemas/CoursePrerequisite" } }, "course_updated_at": { "type": "string", "format": "date-time" } } }, "CourseMetadataUpdate": { "type": "object", "required": ["course_audiences", "course_prerequisites"], "properties": { "course_audiences": { "type": "array", "items": { "$ref": "#/components/schemas/EditCourseAudience" } }, "course_description": { "type": ["string", "null"] }, "course_prerequisites": { "type": "array", "items": { "$ref": "#/components/schemas/EditCoursePrerequisite" } } } }, "CourseModule": { "type": "object", "description": "Like [CourseModulesSchema], but the automatic-completion columns are collapsed into\n`completion_policy`.", "required": [ "id", "created_at", "updated_at", "course_id", "order_number", "completion_policy", "enable_registering_completion_to_uh_open_university", "certification_enabled", "enable_credit_registration_via_suotar" ], "properties": { "certification_enabled": { "type": "boolean" }, "completion_policy": { "$ref": "#/components/schemas/CompletionPolicy" }, "completion_registration_link_override": { "type": ["string", "null"], "description": "If set, use this link rather than the default one when registering course completions." }, "copied_from": { "type": ["string", "null"], "format": "uuid" }, "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "enable_credit_registration_via_suotar": { "type": "boolean" }, "enable_registering_completion_to_uh_open_university": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": ["string", "null"] }, "order_number": { "type": "integer", "format": "int32" }, "uh_course_code": { "type": ["string", "null"] }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseModuleCompletion": { "type": "object", "required": [ "id", "created_at", "updated_at", "course_id", "course_module_id", "user_id", "completion_date", "completion_language", "eligible_for_ects", "email", "passed", "prerequisite_modules_completed", "needs_to_be_reviewed" ], "properties": { "completion_date": { "type": "string", "format": "date-time" }, "completion_granter_user_id": { "type": ["string", "null"], "format": "uuid" }, "completion_language": { "type": "string" }, "completion_registration_attempt_date": { "type": ["string", "null"], "format": "date-time" }, "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "eligible_for_ects": { "type": "boolean" }, "email": { "type": "string" }, "grade": { "type": ["integer", "null"], "format": "int32" }, "id": { "type": "string", "format": "uuid" }, "needs_to_be_reviewed": { "type": "boolean" }, "passed": { "type": "boolean" }, "prerequisite_modules_completed": { "type": "boolean" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "CourseModuleCompletionWithRegistrationInfo": { "type": "object", "required": [ "course_module_id", "created_at", "passed", "prerequisite_modules_completed", "registered", "needs_to_be_reviewed", "user_id", "completion_date" ], "properties": { "completion_date": { "type": "string", "format": "date-time" }, "completion_registration_attempt_date": { "type": ["string", "null"], "format": "date-time", "description": "When the student has attempted to register the completion." }, "course_module_id": { "type": "string", "format": "uuid", "description": "ID of the course module." }, "created_at": { "type": "string", "format": "date-time", "description": "When the record was created" }, "grade": { "type": ["integer", "null"], "format": "int32", "description": "Grade that the student received for the completion." }, "needs_to_be_reviewed": { "type": "boolean", "description": "Whether or not the completion needs to be reviewed by the teacher." }, "passed": { "type": "boolean", "description": "Whether or not the student is eligible for credit for the completion." }, "prerequisite_modules_completed": { "type": "boolean", "description": "Whether or not the student is qualified for credit based on other modules in the course." }, "registered": { "type": "boolean", "description": "Whether or not the completion has been registered to a study registry." }, "user_id": { "type": "string", "format": "uuid", "description": "ID of the user for the completion." } } }, "CourseModuleCreditRegistrationConfig": { "type": "object", "description": "Per-module credit-registration configuration: the rollout switch and the module's own fields\nmerged with its `course_module_suotar_configurations` row. Every field of that row is optional\nhere because a module with no configuration row is a valid, unconfigured module.", "required": ["course_module_id", "course_id", "enable_credit_registration_via_suotar"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "credit_registration_config_check_message": { "type": ["string", "null"] }, "credit_registration_config_checked_at": { "type": ["string", "null"], "format": "date-time" }, "credit_registration_course_code_resolves": { "type": ["boolean", "null"], "description": "`None` means never checked, which is not the same as a failed check." }, "credit_registration_grade_scale_id": { "type": ["string", "null"], "description": "`None` means derive the grade scale from the completion." }, "credit_registration_pause_reason": { "type": ["string", "null"] }, "credit_registration_paused_at": { "type": ["string", "null"], "format": "date-time" }, "credit_registration_paused_by_user_id": { "type": ["string", "null"], "format": "uuid" }, "credit_registration_product_token_found": { "type": ["boolean", "null"] }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "enable_credit_registration_via_suotar": { "type": "boolean" }, "open_university_product_id": { "type": ["string", "null"] }, "uh_course_code": { "type": ["string", "null"] } } }, "CourseModuleCreditRegistrationEdit": { "type": "object", "description": "The module editor's writable half of the Suotar configuration. The pause and the\nconfig-validation verdict are not here: their writers are the admin dashboard and the pipeline.", "required": ["realisations"], "properties": { "grade_scale_id": { "type": ["string", "null"], "description": "`None` means derive the grade scale from the completion." }, "open_university_product_id": { "type": ["string", "null"] }, "realisations": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModuleSuotarRealisationEdit" }, "description": "The full set for the module; anything missing from it is soft-deleted." } } }, "CourseModuleInfo": { "type": "object", "description": "Slim module descriptor so the frontend can label per-module completions and show \"X of Y modules\"\nwithout a separate course-structure fetch. Default (base) module has `name = None`.", "required": [ "id", "order_number", "exercise_count", "daily_submissions", "enable_credit_registration_via_suotar" ], "properties": { "daily_submissions": { "type": "array", "items": { "$ref": "#/components/schemas/DailySubmissionCount" }, "description": "This user's exercise submissions in this module bucketed by UTC day, ascending. Empty if none." }, "ects_credits": { "type": ["number", "null"], "format": "float", "description": "ECTS credits the module is worth. `None` when the module grants no credits." }, "enable_credit_registration_via_suotar": { "type": "boolean" }, "exercise_count": { "type": "integer", "format": "int32", "description": "Number of non-deleted exercises in this module. Submission density is divided by this so courses\nof very different size stay comparable. `0` if the module has no chapter-bound exercises." }, "first_submission_at": { "type": ["string", "null"], "format": "date-time", "description": "Earliest exercise submission by this user in this module. No module \"start\" is stored, so the\nfrontend uses this to infer when an additional module was first worked on. `None` if untouched." }, "id": { "type": "string", "format": "uuid" }, "name": { "type": ["string", "null"] }, "order_number": { "type": "integer", "format": "int32" }, "uh_course_code": { "type": ["string", "null"], "description": "The module's course code in the university's registry, e.g. `BSCS1001`." } } }, "CourseModuleSuotarRealisation": { "type": "object", "required": [ "id", "created_at", "updated_at", "course_module_id", "course_unit_realisation_id", "active", "consecutive_listing_failures" ], "properties": { "active": { "type": "boolean" }, "consecutive_listing_failures": { "type": "integer", "format": "int32" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_unit_realisation_id": { "type": "string" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "label": { "type": ["string", "null"] }, "last_already_linked_count": { "type": ["integer", "null"], "format": "int32" }, "last_fast_track_skipped_account_has_number_count": { "type": ["integer", "null"], "format": "int32" }, "last_fast_track_skipped_name_mismatch_count": { "type": ["integer", "null"], "format": "int32" }, "last_fast_track_skipped_no_account_count": { "type": ["integer", "null"], "format": "int32" }, "last_fast_track_skipped_stale_verification_count": { "type": ["integer", "null"], "format": "int32" }, "last_fast_track_skipped_unlinked_before_count": { "type": ["integer", "null"], "format": "int32" }, "last_fast_track_skipped_unverified_count": { "type": ["integer", "null"], "format": "int32" }, "last_fast_tracked_count": { "type": ["integer", "null"], "format": "int32" }, "last_listed_at": { "type": ["string", "null"], "format": "date-time" }, "last_listed_person_count": { "type": ["integer", "null"], "format": "int32" }, "last_listing_attempted_at": { "type": ["string", "null"], "format": "date-time" }, "last_listing_error": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "last_mailed_count": { "type": ["integer", "null"], "format": "int32" }, "last_no_address_count": { "type": ["integer", "null"], "format": "int32" }, "last_suppressed_by_dedup_count": { "type": ["integer", "null"], "format": "int32" }, "last_suppressed_by_rate_cap_count": { "type": ["integer", "null"], "format": "int32" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseModuleSuotarRealisationEdit": { "type": "object", "required": ["course_unit_realisation_id", "active"], "properties": { "active": { "type": "boolean" }, "course_unit_realisation_id": { "type": "string" }, "label": { "type": ["string", "null"], "description": "Rendered to students as the name of the realisation their credits go against." } } }, "CourseModuleThresholdInfo": { "type": "object", "description": "Per-module threshold configuration plus the policy-derived limits the configuration UI needs to\nrender and validate the threshold form. Computed server-side so the exemption rule and the\nminimum/default values live in one place instead of being duplicated in the frontend.", "required": ["course_module_id", "minimum_duration_seconds", "default_duration_seconds"], "properties": { "configured_duration_seconds": { "type": ["integer", "null"], "format": "int32", "description": "The explicitly configured threshold in seconds, or `None` when the module has no threshold\nrow and [`Self::default_duration_seconds`] applies." }, "course_module_id": { "type": "string", "format": "uuid" }, "default_duration_seconds": { "type": "integer", "format": "int32", "description": "The threshold applied when none is configured." }, "minimum_duration_seconds": { "type": "integer", "format": "int32", "description": "The smallest threshold a teacher may save for this module: `0` for small (exempt) modules,\notherwise [`MINIMUM_CHEATER_THRESHOLD_SECONDS`]." } } }, "CoursePrerequisite": { "type": "object", "required": ["id", "created_at", "updated_at", "course_id", "prerequisite"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "embedding": { "type": ["array", "null"], "items": { "type": "number", "format": "float" } }, "id": { "type": "string", "format": "uuid" }, "prerequisite": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "CourseStructure": { "type": "object", "required": ["course", "pages", "chapters", "modules"], "properties": { "chapters": { "type": "array", "items": { "$ref": "#/components/schemas/Chapter" } }, "course": { "$ref": "#/components/schemas/Course" }, "modules": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModule" } }, "pages": { "type": "array", "items": { "$ref": "#/components/schemas/Page" } } } }, "CourseStudentListRow": { "type": "object", "description": "One row of the paginated student identity list (one row per distinct enrolled user).", "required": ["user_id", "course_instances", "has_active_instance"], "properties": { "course_instances": { "type": "array", "items": { "type": "string" }, "description": "Names of the non-deleted course instances the user is enrolled in for this course." }, "email": { "type": ["string", "null"] }, "first_name": { "type": ["string", "null"] }, "has_active_instance": { "type": "boolean", "description": "Whether the user has any enrollment into a non-deleted instance. Separates the unnamed default\ninstance (true) from a since-deleted instance (false) when `course_instances` is empty." }, "last_name": { "type": ["string", "null"] }, "user_id": { "type": "string", "format": "uuid" } } }, "CourseStudentsProgressStructure": { "type": "object", "description": "Course-level progress structure for the Progress tab. Does not depend on which students are on\nthe current page, so it is fetched once and cached per course (not per identity page).", "required": ["chapter_locking_enabled", "chapters", "chapter_availability"], "properties": { "chapter_availability": { "type": "array", "items": { "$ref": "#/components/schemas/ChapterAvailability" } }, "chapter_locking_enabled": { "type": "boolean" }, "chapters": { "type": "array", "items": { "$ref": "#/components/schemas/DatabaseChapter" } } } }, "CourseStudentsProgressUsers": { "type": "object", "description": "Per-user progress detail for the Progress tab, scoped to the requested `user_ids`.", "required": ["user_chapter_progress", "user_chapter_locking_statuses"], "properties": { "user_chapter_locking_statuses": { "type": "array", "items": { "$ref": "#/components/schemas/UserChapterLockingStatus" } }, "user_chapter_progress": { "type": "array", "items": { "$ref": "#/components/schemas/UserChapterProgress" } } } }, "CourseUpdate": { "type": "object", "required": [ "name", "is_draft", "is_test_mode", "can_add_chatbot", "is_unlisted", "is_joinable_by_code_only", "ask_marketing_consent", "flagged_answers_threshold", "flagged_answers_skip_manual_review_and_allow_retry", "chapter_locking_enabled", "ai_policy" ], "properties": { "ai_policy": { "$ref": "#/components/schemas/CourseAiPolicy" }, "ask_marketing_consent": { "type": "boolean" }, "can_add_chatbot": { "type": "boolean" }, "chapter_locking_enabled": { "type": "boolean" }, "closed_additional_message": { "type": ["string", "null"] }, "closed_at": { "type": ["string", "null"], "format": "date-time" }, "closed_course_successor_id": { "type": ["string", "null"], "format": "uuid" }, "course_material_ai_instructions": { "type": ["boolean", "null"] }, "description": { "type": ["string", "null"] }, "flagged_answers_skip_manual_review_and_allow_retry": { "type": "boolean" }, "flagged_answers_threshold": { "type": "integer", "format": "int32" }, "is_draft": { "type": "boolean" }, "is_joinable_by_code_only": { "type": "boolean" }, "is_test_mode": { "type": "boolean" }, "is_unlisted": { "type": "boolean" }, "name": { "type": "string" } } }, "CreateChatbotRequest": { "type": "object", "required": ["name"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "name": { "type": "string" } } }, "CreateCourseDesignerPlanRequest": { "type": "object", "properties": { "name": { "type": ["string", "null"] } } }, "CreateCourseDesignerStageTaskRequest": { "type": "object", "required": ["title"], "properties": { "description": { "type": ["string", "null"] }, "title": { "type": "string" } } }, "CreditRegistrationAdminAction": { "type": "string", "enum": [ "retry_item", "retry_failed_for_course", "force_recheck", "mark_resolved", "requeue_batch", "transition_item", "cancel_registration", "pause_course_module", "resume_course_module", "pause_phase", "resume_phase", "run_phase_now", "resend_link_email", "unlink_student_number", "manual_link_student_number", "override_rate_cap" ] }, "CreditRegistrationAdminActionRecord": { "type": "object", "required": [ "id", "created_at", "updated_at", "action", "target_kind", "actor_user_id", "actor_role" ], "properties": { "action": { "$ref": "#/components/schemas/CreditRegistrationAdminAction" }, "actor_course_id": { "type": ["string", "null"], "format": "uuid" }, "actor_role": { "type": "string" }, "actor_user_id": { "type": "string", "format": "uuid" }, "affected_row_count": { "type": ["integer", "null"], "format": "int32" }, "after_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "before_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "details": {}, "id": { "type": "string", "format": "uuid" }, "reason": { "type": ["string", "null"] }, "target_id": { "type": ["string", "null"], "format": "uuid" }, "target_kind": { "$ref": "#/components/schemas/CreditRegistrationAdminActionTarget" }, "target_phase": { "type": ["string", "null"] }, "updated_at": { "type": "string", "format": "date-time" } } }, "CreditRegistrationAdminActionRow": { "type": "object", "required": ["id", "created_at", "action", "target_kind", "actor_user_id", "actor_role"], "properties": { "action": { "$ref": "#/components/schemas/CreditRegistrationAdminAction" }, "actor_course_id": { "type": ["string", "null"], "format": "uuid", "description": "The course whose edit permission authorised a teacher action." }, "actor_email": { "type": ["string", "null"] }, "actor_first_name": { "type": ["string", "null"] }, "actor_last_name": { "type": ["string", "null"] }, "actor_role": { "type": "string", "description": "`global_admin` or `course_teacher`." }, "actor_user_id": { "type": "string", "format": "uuid" }, "affected_row_count": { "type": ["integer", "null"], "format": "int32" }, "after_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "before_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "course_name": { "type": ["string", "null"] }, "created_at": { "type": "string", "format": "date-time" }, "details": {}, "id": { "type": "string", "format": "uuid" }, "reason": { "type": ["string", "null"] }, "target_id": { "type": ["string", "null"], "format": "uuid", "description": "`None` for a phase target, which is named by `target_phase`, and for a bulk action over a\nselection, whose ids are in `details`." }, "target_kind": { "$ref": "#/components/schemas/CreditRegistrationAdminActionTarget" }, "target_phase": { "type": ["string", "null"] } } }, "CreditRegistrationAdminActionTarget": { "type": "string", "enum": [ "credit_registration", "course_module", "course", "phase", "verified_student_number", "student_number_verification_token" ] }, "CreditRegistrationAdminActionsPage": { "type": "object", "required": ["data", "total_count", "total_pages"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationAdminActionRow" } }, "total_count": { "type": "integer", "format": "int64" }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 } } }, "CreditRegistrationAlert": { "type": "object", "required": ["id", "severity", "count"], "properties": { "at": { "type": ["string", "null"], "format": "date-time", "description": "When it last happened, where the rule has an instant to point at." }, "count": { "type": "integer", "format": "int64", "description": "How many rows, calls or phases the rule found." }, "id": { "$ref": "#/components/schemas/CreditRegistrationAlertId" }, "severity": { "$ref": "#/components/schemas/CreditRegistrationAlertSeverity" }, "subject": { "type": ["string", "null"], "description": "An identifier the operator can act on — a phase name, a ledger state, a mail domain. Never a\nsentence, and never anything the study registry wrote." }, "total": { "type": ["integer", "null"], "format": "int64", "description": "What `count` is out of, where the rule measured one. Not a threshold: thresholds are the\nsame for every evaluation and travel separately." } } }, "CreditRegistrationAlertId": { "type": "string", "enum": [ "credentials_rejected", "study_registry_unreachable", "sisu_unavailable", "stuck_registrations", "linking_mail_send_failed", "linking_mail_rate_cap_exceeded", "phase_heartbeat_stale", "phase_failing", "permanent_failures_accumulating", "misregistrations_detected", "course_configuration_broken", "pipeline_idle", "completions_never_entered", "confirmation_latency_regressed", "fast_track_name_mismatch", "pipeline_paused_globally" ] }, "CreditRegistrationAlertSeverity": { "type": "string", "enum": ["info", "warning", "critical"] }, "CreditRegistrationAlertThresholds": { "type": "object", "description": "Every number the rules used, so the UI can say \"N of M\" without holding a copy of M.", "required": [ "credential_rejection_window_secs", "unreachable_window_secs", "unreachable_consecutive_failures", "sisu_outage_window_secs", "sisu_outage_min_items", "sisu_outage_failure_share_percent", "stuck_ready_to_submit_secs", "stuck_submitting_secs", "stuck_awaiting_verification_secs", "stuck_failed_retryable_secs", "stuck_critical_count", "linking_mail_window_secs", "linking_mail_hourly_cap", "phase_heartbeat_interval_multiplier", "phase_consecutive_failure_limit", "phase_success_interval_multiplier", "terminal_window_secs", "permanent_failure_count", "permanent_failure_rate_percent", "misregistration_critical_count", "idle_queue_depth", "never_entered_min_age_secs", "latency_window_secs", "latency_regression_floor_secs", "latency_regression_factor", "fast_track_name_mismatch_count" ], "properties": { "credential_rejection_window_secs": { "type": "integer", "format": "int64" }, "fast_track_name_mismatch_count": { "type": "integer", "format": "int64" }, "idle_queue_depth": { "type": "integer", "format": "int64" }, "latency_regression_factor": { "type": "integer", "format": "int64" }, "latency_regression_floor_secs": { "type": "integer", "format": "int64" }, "latency_window_secs": { "type": "integer", "format": "int64" }, "linking_mail_hourly_cap": { "type": "integer", "format": "int64" }, "linking_mail_window_secs": { "type": "integer", "format": "int64" }, "misregistration_critical_count": { "type": "integer", "format": "int64" }, "never_entered_min_age_secs": { "type": "integer", "format": "int64" }, "permanent_failure_count": { "type": "integer", "format": "int64" }, "permanent_failure_rate_percent": { "type": "integer", "format": "int64" }, "phase_consecutive_failure_limit": { "type": "integer", "format": "int32" }, "phase_heartbeat_interval_multiplier": { "type": "integer", "format": "int32" }, "phase_success_interval_multiplier": { "type": "integer", "format": "int32" }, "sisu_outage_failure_share_percent": { "type": "integer", "format": "int64" }, "sisu_outage_min_items": { "type": "integer", "format": "int64" }, "sisu_outage_window_secs": { "type": "integer", "format": "int64" }, "stuck_awaiting_verification_secs": { "type": "integer", "format": "int64" }, "stuck_critical_count": { "type": "integer", "format": "int64" }, "stuck_failed_retryable_secs": { "type": "integer", "format": "int64" }, "stuck_ready_to_submit_secs": { "type": "integer", "format": "int64" }, "stuck_submitting_secs": { "type": "integer", "format": "int64" }, "terminal_window_secs": { "type": "integer", "format": "int64" }, "unreachable_consecutive_failures": { "type": "integer", "format": "int64" }, "unreachable_window_secs": { "type": "integer", "format": "int64" } } }, "CreditRegistrationAttentionItem": { "type": "object", "required": [ "credit_registration_id", "user_id", "course_id", "course_name", "course_module_id", "state", "state_entered_at", "attempt_count", "next_attempt_at", "reasons" ], "properties": { "attempt_count": { "type": "integer", "format": "int32" }, "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "course_name": { "type": "string" }, "credit_registration_id": { "type": "string", "format": "uuid" }, "email": { "type": ["string", "null"], "description": "In full: this is the list support works from." }, "error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "first_name": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "next_attempt_at": { "type": "string", "format": "date-time" }, "reasons": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationAttentionReason" }, "description": "Every detector that picked this row, so the table can group by any of them." }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" }, "state_entered_at": { "type": "string", "format": "date-time" }, "student_number": { "type": ["string", "null"] }, "user_id": { "type": "string", "format": "uuid" } } }, "CreditRegistrationAttentionItems": { "type": "object", "required": ["items", "total_count", "counts_by_reason", "max_items"], "properties": { "counts_by_reason": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationAttentionReasonCount" } }, "items": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationAttentionItem" } }, "max_items": { "type": "integer", "format": "int64" }, "total_count": { "type": "integer", "format": "int64", "description": "Rows returned, which is the tab badge. Capped at `max_items`; the counts per reason are over\nthe same capped set." } } }, "CreditRegistrationAttentionReason": { "type": "string", "description": "Why a row is on the attention table. One row can carry several.", "enum": [ "stuck_in_state", "permanent_error", "retry_window_expired", "misregistered", "too_many_attempts", "outcome_uncertain", "flagged_by_pipeline" ] }, "CreditRegistrationAttentionReasonCount": { "type": "object", "required": ["reason", "count"], "properties": { "count": { "type": "integer", "format": "int64" }, "reason": { "$ref": "#/components/schemas/CreditRegistrationAttentionReason" } } }, "CreditRegistrationCircuitBreakerState": { "type": "object", "description": "The circuit breaker as this web process holds it. The global key only — a narrowed run gets its own\n— and the counters live in process memory, so this says whether this server would currently skip a\nstudy registry call, not whether the workers would.", "required": ["open", "consecutive_failures", "trips_after_consecutive_failures"], "properties": { "consecutive_failures": { "type": "integer", "format": "int64" }, "open": { "type": "boolean" }, "open_for_secs": { "type": ["integer", "null"], "format": "int64" }, "trips_after_consecutive_failures": { "type": "integer", "format": "int64" } } }, "CreditRegistrationConsentModule": { "type": "object", "required": ["id"], "properties": { "ects_credits": { "type": ["number", "null"], "format": "float" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": ["string", "null"] }, "uh_course_code": { "type": ["string", "null"] } } }, "CreditRegistrationCourseConfigCheck": { "type": "object", "description": "What the configuration check concluded about one module, freshly derived from the same facts and\nthe same rule the `config-validation` phase uses.\n\n`course_code_resolves` is `None` while no listing has been attempted: never checked is not the\nsame as checked and failed, and the two must not render alike.", "properties": { "course_code_resolves": { "type": ["boolean", "null"] }, "message": { "type": ["string", "null"], "description": "Every problem found, in one line. `None` means the module is fine." }, "product_token_found": { "type": ["boolean", "null"] } } }, "CreditRegistrationCourseStats": { "type": "object", "required": [ "course_id", "course_name", "course_module_id", "old_flow_also_enabled", "active_realisation_count", "check", "eligible_completion_count", "registration_count", "success_count", "in_flight_count", "failed_count", "abandoned_count", "needs_admin_attention_count", "awaiting_consent_count" ], "properties": { "abandoned_count": { "type": "integer", "format": "int64", "description": "Neither a success nor a failure, and shown separately so it is never read as one." }, "active_realisation_count": { "type": "integer", "format": "int64" }, "awaiting_consent_count": { "type": "integer", "format": "int64" }, "check": { "$ref": "#/components/schemas/CreditRegistrationCourseConfigCheck", "description": "What the current facts say. Recomputed on read, so a configuration fixed a minute ago no\nlonger shows as broken." }, "config_checked_at": { "type": ["string", "null"], "format": "date-time", "description": "When the `config-validation` phase last stamped its verdict on the row. `None` means never,\nwhich the stored verdict beside it cannot express on its own." }, "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "course_name": { "type": "string" }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "eligible_completion_count": { "type": "integer", "format": "int64", "description": "Completions `materialize` would take. Against `registration_count` this is the backfill\nprogress: a gap that stops closing is the actionable signal." }, "failed_count": { "type": "integer", "format": "int64" }, "grade_scale_id": { "type": ["string", "null"], "description": "The module's override; `None` means the scale is derived from the completion." }, "in_flight_count": { "type": "integer", "format": "int64" }, "last_listed_at": { "type": ["string", "null"], "format": "date-time" }, "last_registered_at": { "type": ["string", "null"], "format": "date-time" }, "needs_admin_attention_count": { "type": "integer", "format": "int64" }, "old_flow_also_enabled": { "type": "boolean", "description": "The old pull path is on as well, which would register the same completion twice." }, "open_university_product_id": { "type": ["string", "null"] }, "pause_reason": { "type": ["string", "null"] }, "paused_at": { "type": ["string", "null"], "format": "date-time" }, "registration_count": { "type": "integer", "format": "int64" }, "stored_config_check_message": { "type": ["string", "null"], "description": "The verdict as the phase stored it, which may be older than `check`." }, "success_count": { "type": "integer", "format": "int64" }, "top_error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "uh_course_code": { "type": ["string", "null"] } } }, "CreditRegistrationDetails": { "type": "object", "required": ["course_id", "course_name", "registration", "attempts", "events"], "properties": { "attempts": { "type": "array", "items": { "$ref": "#/components/schemas/CourseCreditRegistration" }, "description": "Every attempt for the same completion, newest first, this one included." }, "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "events": { "type": "array", "items": { "$ref": "#/components/schemas/CourseCreditRegistrationEvent" } }, "not_improved_attainment": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/NotImprovedAttainment", "description": "The grade the registry already held, for a row it declined as no improvement. The\nregistration's own grade is what we sent." } ] }, "registration": { "$ref": "#/components/schemas/CourseCreditRegistration" } } }, "CreditRegistrationErrorCode": { "type": "string", "description": "Why a ledger row is where it is; `state` says what happens to it next.", "enum": [ "person_not_found", "course_code_not_found", "enrolment_not_found", "enrolment_not_accepted", "invalid_grade_for_grade_scale", "course_not_allowed", "invalid_credits", "study_right_not_valid", "acceptor_not_found", "sisu_validation_failed", "sisu_timeout", "sisu_temporarily_unavailable", "misregistered", "unauthorized", "malformed_request", "transport_error", "unexpected_response", "no_grade_scale_mapping", "missing_uh_course_code", "missing_ects_credits", "retry_window_expired", "unknown" ] }, "CreditRegistrationErrorCodeTotal": { "type": "object", "required": ["error_code", "in_flight_count", "terminal_failure_count"], "properties": { "error_code": { "$ref": "#/components/schemas/CreditRegistrationErrorCode" }, "in_flight_count": { "type": "integer", "format": "int64", "description": "Rows the pipeline is still working on." }, "terminal_failure_count": { "type": "integer", "format": "int64", "description": "Rows that ended on this code." } } }, "CreditRegistrationErrorCodeWindow": { "type": "object", "description": "One error code over the chosen window and the one before it.", "required": [ "error_code", "retryability", "current_count", "previous_count", "user_count", "course_count", "endpoints" ], "properties": { "course_count": { "type": "integer", "format": "int64" }, "current_count": { "type": "integer", "format": "int64" }, "endpoints": { "type": "array", "items": { "$ref": "#/components/schemas/SuotarEndpoint" } }, "error_code": { "$ref": "#/components/schemas/CreditRegistrationErrorCode" }, "first_seen_at": { "type": ["string", "null"], "format": "date-time" }, "last_seen_at": { "type": ["string", "null"], "format": "date-time" }, "previous_count": { "type": "integer", "format": "int64" }, "retryability": { "$ref": "#/components/schemas/Retryability", "description": "What may be done about the code, which is the difference between a wait and a fix." }, "user_count": { "type": "integer", "format": "int64" } } }, "CreditRegistrationErrorsByCode": { "type": "object", "required": ["window_secs", "codes", "verdicts"], "properties": { "codes": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationErrorCodeWindow" } }, "verdicts": { "$ref": "#/components/schemas/CreditRegistrationTerminalVerdicts" }, "window_secs": { "type": "integer", "format": "int64" } } }, "CreditRegistrationEventKind": { "type": "string", "enum": [ "created", "state_changed", "suotar_response", "retry_scheduled", "admin_action", "student_action", "cancelled" ] }, "CreditRegistrationHealth": { "type": "object", "required": ["status", "alerts", "thresholds"], "properties": { "alerts": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationAlert" }, "description": "Critical first, and a rejected credential first of all: nothing registers until it is fixed." }, "status": { "$ref": "#/components/schemas/HealthStatus" }, "thresholds": { "$ref": "#/components/schemas/CreditRegistrationAlertThresholds" } } }, "CreditRegistrationHistory": { "type": "object", "required": ["days", "from", "to"], "properties": { "days": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationHistoryDay" }, "description": "Oldest first. Days with no snapshot are absent rather than zeroed: before the phase first\nran, and on a day it did not, there is no depth to report." }, "from": { "type": "string", "format": "date" }, "to": { "type": "string", "format": "date" } } }, "CreditRegistrationHistoryDay": { "type": "object", "required": ["snapshot_date", "states"], "properties": { "snapshot_date": { "type": "string", "format": "date", "description": "The UTC day the snapshot describes." }, "states": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationHistoryPoint" }, "description": "Every state, whether or not anything is in it: a missing state would read as a gap in the\nchart rather than as an empty queue." } } }, "CreditRegistrationHistoryPoint": { "type": "object", "description": "One state's depth and flow on one day.", "required": ["state", "count", "entered_count", "left_count"], "properties": { "count": { "type": "integer", "format": "int32", "description": "Rows in the state when the snapshot was taken, once that day." }, "entered_count": { "type": "integer", "format": "int32", "description": "Transitions into and out of the state during that UTC day. A self-transition is neither." }, "left_count": { "type": "integer", "format": "int32" }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" } } }, "CreditRegistrationNotificationKind": { "type": "string", "description": "Which of the two student mails a row is owed, or already holds.", "enum": ["action_needed", "registered"] }, "CreditRegistrationOldestNonTerminal": { "type": "object", "required": ["credit_registration_id", "state", "state_entered_at"], "properties": { "credit_registration_id": { "type": "string", "format": "uuid" }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" }, "state_entered_at": { "type": "string", "format": "date-time" } } }, "CreditRegistrationOverview": { "type": "object", "required": [ "health", "counts_by_state", "error_codes", "needs_admin_attention_count", "throughput", "throughput_days", "stuck", "endpoints", "circuit_breaker", "phases" ], "properties": { "circuit_breaker": { "$ref": "#/components/schemas/CreditRegistrationCircuitBreakerState" }, "counts_by_state": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationStateTotal" } }, "endpoints": { "type": "array", "items": { "$ref": "#/components/schemas/SuotarEndpointStanding" } }, "error_codes": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationErrorCodeTotal" } }, "health": { "$ref": "#/components/schemas/CreditRegistrationHealth" }, "needs_admin_attention_count": { "type": "integer", "format": "int64" }, "oldest_non_terminal": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationOldestNonTerminal" } ] }, "phases": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationPhaseStatus" } }, "stuck": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationStuckTotal" } }, "throughput": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationThroughputBucket" } }, "throughput_days": { "type": "integer", "format": "int64" } } }, "CreditRegistrationPhaseList": { "type": "object", "required": [ "phases", "heartbeat_interval_multiplier", "consecutive_failure_limit", "paused_globally" ], "properties": { "consecutive_failure_limit": { "type": "integer", "format": "int32" }, "heartbeat_interval_multiplier": { "type": "integer", "format": "int32" }, "paused_globally": { "type": "boolean", "description": "Every phase is stopped, which is what the kill switch does." }, "phases": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationPhaseRow" }, "description": "In process then pipeline order, so the grouping is a fold over the list." } } }, "CreditRegistrationPhaseRow": { "type": "object", "description": "One phase as the Workers tab renders it.\n\nWider than the Overview strip's `CreditRegistrationPhaseStatus`: this one carries the last error,\nthe run window and the queue, which the Overview deliberately leaves out.", "required": [ "phase", "process_name", "expected_interval_secs", "consecutive_failures", "implemented", "heartbeat_late", "failing", "owned_states" ], "properties": { "consecutive_failures": { "type": "integer", "format": "int32" }, "expected_interval_secs": { "type": "integer", "format": "int32" }, "failing": { "type": "boolean" }, "heartbeat_late": { "type": "boolean", "description": "Always `false` while paused or never heartbeated." }, "implemented": { "type": "boolean", "description": "No implementation is registered for the phase yet, so it has never reported and will not." }, "items_failed_last_run": { "type": ["integer", "null"], "format": "int32" }, "items_processed_last_run": { "type": ["integer", "null"], "format": "int32" }, "last_error": { "type": ["string", "null"], "description": "Our own wording or the study registry's code, never its prose." }, "last_heartbeat_at": { "type": ["string", "null"], "format": "date-time" }, "last_run_duration_secs": { "type": ["integer", "null"], "format": "int64" }, "last_run_finished_at": { "type": ["string", "null"], "format": "date-time" }, "last_run_started_at": { "type": ["string", "null"], "format": "date-time" }, "last_success_at": { "type": ["string", "null"], "format": "date-time" }, "next_run_at": { "type": ["string", "null"], "format": "date-time" }, "owned_states": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationState" }, "description": "The ledger states nothing but this phase moves a row out of. Empty for the phases whose work\nis not a ledger state: `materialize` waits on completions, the syncer's phases on modules." }, "pause_reason": { "type": ["string", "null"] }, "paused_at": { "type": ["string", "null"], "format": "date-time" }, "paused_by_user_id": { "type": ["string", "null"], "format": "uuid" }, "phase": { "type": "string" }, "process_name": { "type": "string", "description": "The worker process whose loop runs the phase. Rows are grouped by it, because a dead pod\nmakes every phase inside it go stale at once and that reads as one fault, not seven." }, "queue_depth": { "type": ["integer", "null"], "format": "int64", "description": "Live rows in `owned_states`, or `None` where there are none to own — which is not the same\nas an empty queue." }, "seconds_since_heartbeat": { "type": ["integer", "null"], "format": "int64", "description": "Computed server-side: a page comparing its own clock against a server timestamp misjudges\nthis on a skewed client." } } }, "CreditRegistrationPhaseStatus": { "type": "object", "description": "One pipeline phase's heartbeat, written by the worker loops and by unscoped runs only, never by a\nnarrowed one.", "required": [ "phase", "process_name", "expected_interval_secs", "consecutive_failures", "implemented", "heartbeat_late" ], "properties": { "consecutive_failures": { "type": "integer", "format": "int32" }, "expected_interval_secs": { "type": "integer", "format": "int32" }, "heartbeat_late": { "type": "boolean", "description": "`seconds_since_heartbeat > expected_interval_secs * health.thresholds.phase_heartbeat_interval_multiplier`.\nAlways `false` while paused or never heartbeated." }, "implemented": { "type": "boolean", "description": "No implementation is registered for the phase yet, so it has never reported and will not." }, "items_failed_last_run": { "type": ["integer", "null"], "format": "int32" }, "items_processed_last_run": { "type": ["integer", "null"], "format": "int32" }, "last_heartbeat_at": { "type": ["string", "null"], "format": "date-time" }, "last_run_finished_at": { "type": ["string", "null"], "format": "date-time" }, "last_success_at": { "type": ["string", "null"], "format": "date-time" }, "pause_reason": { "type": ["string", "null"] }, "paused_at": { "type": ["string", "null"], "format": "date-time" }, "phase": { "type": "string" }, "process_name": { "type": "string" }, "seconds_since_heartbeat": { "type": ["integer", "null"], "format": "int64", "description": "Computed server-side: a page comparing its own clock against a server timestamp misjudges this\non a skewed client." } } }, "CreditRegistrationReconciliation": { "type": "object", "required": [ "never_entered", "outcome_uncertain", "several_submitted_attainments", "misregistered", "legacy_divergences", "outcome_unknown_consent_withdrawn", "never_entered_count", "outcome_uncertain_count", "several_submitted_attainments_count", "misregistered_count", "legacy_divergence_count", "outcome_unknown_consent_withdrawn_count", "finding_count", "max_rows_per_detector" ], "properties": { "finding_count": { "type": "integer", "format": "int64", "description": "The five detector counts, which is the tab badge. The consent-withdrawal bucket is\ndeliberately outside it." }, "legacy_divergence_count": { "type": "integer", "format": "int64" }, "legacy_divergences": { "type": "array", "items": { "$ref": "#/components/schemas/LegacyLedgerDivergenceRow" } }, "max_rows_per_detector": { "type": "integer", "format": "int64" }, "misregistered": { "type": "array", "items": { "$ref": "#/components/schemas/ReconciliationRegistration" }, "description": "Attainments the study registry reversed after we had recorded them as registered." }, "misregistered_count": { "type": "integer", "format": "int64" }, "never_entered": { "type": "array", "items": { "$ref": "#/components/schemas/NeverEnteredCompletion" }, "description": "Eligible completions with no ledger row at all." }, "never_entered_count": { "type": "integer", "format": "int64", "description": "The detectors' counts, in the same order as the lists, capped at `max_rows_per_detector`." }, "outcome_uncertain": { "type": "array", "items": { "$ref": "#/components/schemas/ReconciliationRegistration" }, "description": "`submission_uncertain`: the import may or may not have landed. Verify these, never resubmit." }, "outcome_uncertain_count": { "type": "integer", "format": "int64" }, "outcome_unknown_consent_withdrawn": { "type": "array", "items": { "$ref": "#/components/schemas/ReconciliationRegistration" }, "description": "**Not an error list and not a work queue.** These students withdrew consent while a\nregistration was in flight, so we stopped polling and do not know what the registry did. It\nis here so the number is never mistaken for a failure; it is in no count above and in no\nalert." }, "outcome_unknown_consent_withdrawn_count": { "type": "integer", "format": "int64" }, "several_submitted_attainments": { "type": "array", "items": { "$ref": "#/components/schemas/ReconciliationRegistration" }, "description": "Rows whose answers named more than one submitted attainment id, which is what a double\nsubmission would look like." }, "several_submitted_attainments_count": { "type": "integer", "format": "int64" } } }, "CreditRegistrationState": { "type": "string", "description": "What the pipeline does next with a ledger row.", "enum": [ "pending_prerequisites", "pending_consent", "pending_student_number", "ready_to_submit", "resolving_enrolment", "checking_enrolment", "no_usable_enrolment", "submitting", "submission_uncertain", "awaiting_verification", "registered", "duplicate", "not_improved", "misregistered", "failed_retryable", "failed_permanent", "blocked", "cancelled", "abandoned_by_consent_withdrawal" ] }, "CreditRegistrationStateCount": { "type": "object", "required": ["state", "count"], "properties": { "count": { "type": "integer", "format": "int64" }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" } } }, "CreditRegistrationStateTotal": { "type": "object", "required": ["state", "count"], "properties": { "count": { "type": "integer", "format": "int64" }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" } } }, "CreditRegistrationStatsByCourse": { "type": "object", "required": ["modules", "misconfigured_count"], "properties": { "misconfigured_count": { "type": "integer", "format": "int64", "description": "Modules whose current facts fail the check, which is the tab badge." }, "modules": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationCourseStats" } } } }, "CreditRegistrationStuckTotal": { "type": "object", "required": ["state", "count", "severely_stuck_count"], "properties": { "count": { "type": "integer", "format": "int64" }, "oldest_state_entered_at": { "type": ["string", "null"], "format": "date-time" }, "severely_stuck_count": { "type": "integer", "format": "int64" }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" } } }, "CreditRegistrationTerminalVerdicts": { "type": "object", "description": "The verdicts an operator needs beside the errors to rule them out. `not_improved` and\n`abandoned_by_consent_withdrawal` are not failures and are never in the error table above.", "required": [ "registered_count", "duplicate_and_not_improved_count", "failed_permanent_count", "cancelled_count", "abandoned_by_consent_withdrawal_count", "total_count" ], "properties": { "abandoned_by_consent_withdrawal_count": { "type": "integer", "format": "int64" }, "cancelled_count": { "type": "integer", "format": "int64" }, "duplicate_and_not_improved_count": { "type": "integer", "format": "int64" }, "failed_permanent_count": { "type": "integer", "format": "int64" }, "registered_count": { "type": "integer", "format": "int64" }, "total_count": { "type": "integer", "format": "int64", "description": "Everything but the abandoned: the denominator of the success rate." } } }, "CreditRegistrationThroughputBucket": { "type": "object", "required": ["day", "registered_count", "other_success_count", "failed_count"], "properties": { "day": { "type": "string", "format": "date-time" }, "failed_count": { "type": "integer", "format": "int64" }, "other_success_count": { "type": "integer", "format": "int64", "description": "`duplicate` and `not_improved`: the credit exists, and we did not put it there." }, "registered_count": { "type": "integer", "format": "int64" } } }, "CronJobInfo": { "type": "object", "required": ["name", "schedule"], "properties": { "last_schedule_time": { "type": ["string", "null"] }, "name": { "type": "string" }, "schedule": { "type": "string" } } }, "DailySubmissionCount": { "type": "object", "description": "One UTC day's exercise-submission count for a module, used for the activity-density violins on the\ncross-course timeline. `day` is midnight of the day the submissions fall in.", "required": ["day", "count"], "properties": { "count": { "type": "integer", "format": "int32" }, "day": { "type": "string", "format": "date-time" } } }, "DatabaseChapter": { "type": "object", "required": [ "id", "created_at", "updated_at", "name", "course_id", "chapter_number", "course_module_id" ], "properties": { "chapter_image_path": { "type": ["string", "null"] }, "chapter_number": { "type": "integer", "format": "int32" }, "color": { "type": ["string", "null"] }, "copied_from": { "type": ["string", "null"], "format": "uuid" }, "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deadline": { "type": ["string", "null"], "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "front_page_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "opens_at": { "type": ["string", "null"], "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" } } }, "DatabaseOrganization": { "type": "object", "required": ["id", "slug", "created_at", "updated_at", "name", "hidden"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "description": { "type": ["string", "null"] }, "hidden": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "organization_image_path": { "type": ["string", "null"] }, "slug": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "DeploymentInfo": { "type": "object", "required": ["name", "replicas", "ready_replicas", "selector_labels"], "properties": { "name": { "type": "string" }, "ready_replicas": { "type": "integer", "format": "int32" }, "replicas": { "type": "integer", "format": "int32" }, "selector_labels": { "type": "object", "additionalProperties": { "type": "string" }, "propertyNames": { "type": "string" } } } }, "DeviceAuthorizationForm": { "type": "object", "description": "Form body for `POST /device_authorization` (RFC 8628 §3.1).", "required": ["client_id"], "properties": { "client_id": { "type": "string" }, "scope": { "type": ["string", "null"], "description": "Space-delimited requested scopes. Optional; when absent the client's\nregistered scopes are used." } } }, "DeviceAuthorizationResponse": { "type": "object", "description": "Success body for `POST /device_authorization` (RFC 8628 §3.2).", "required": [ "device_code", "user_code", "verification_uri", "verification_uri_complete", "expires_in", "interval" ], "properties": { "device_code": { "type": "string" }, "expires_in": { "type": "integer", "format": "int64" }, "interval": { "type": "integer", "format": "int32" }, "user_code": { "type": "string" }, "verification_uri": { "type": "string" }, "verification_uri_complete": { "type": "string" } } }, "DeviceDecisionBody": { "type": "object", "description": "Body for the approve/deny verification actions.", "required": ["user_code"], "properties": { "user_code": { "type": "string" } } }, "DeviceDecisionResponse": { "type": "object", "description": "Result of an approve/deny action.", "required": ["status"], "properties": { "status": { "type": "string", "description": "`\"approved\"` or `\"denied\"`." } } }, "DeviceVerificationInfo": { "type": "object", "description": "Render data returned to the verification page so it can show the user what\nthey are about to authorize.", "required": ["client_id", "client_name", "scopes", "user_code"], "properties": { "client_id": { "type": "string", "description": "Public client identifier (the `client_id` string, not the internal UUID)." }, "client_name": { "type": "string", "description": "Human-readable client name for display." }, "scopes": { "type": "array", "items": { "type": "string" }, "description": "Scopes the client is requesting." }, "user_code": { "type": "string", "description": "The normalized `user_code` (`XXXX-XXXX`), echoed back for display." } } }, "DomainCompletionStats": { "type": "object", "required": [ "email_domain", "total_completions", "unique_users", "registered_completions", "not_registered_completions", "users_with_some_registered_completions", "users_with_some_unregistered_completions", "registered_ects_credits", "not_registered_ects_credits" ], "properties": { "email_domain": { "type": "string" }, "not_registered_completions": { "type": "integer", "format": "int64" }, "not_registered_ects_credits": { "type": "number", "format": "float" }, "registered_completion_percentage": { "type": ["number", "null"], "format": "double" }, "registered_completions": { "type": "integer", "format": "int64" }, "registered_ects_credits": { "type": "number", "format": "float" }, "total_completions": { "type": "integer", "format": "int64" }, "unique_users": { "type": "integer", "format": "int64" }, "users_with_some_registered_completions": { "type": "integer", "format": "int64" }, "users_with_some_unregistered_completions": { "type": "integer", "format": "int64" } } }, "EditCourseAudience": { "type": "object", "required": ["id", "course_id", "audience"], "properties": { "audience": { "type": "string" }, "course_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" } } }, "EditCoursePrerequisite": { "type": "object", "required": ["id", "course_id", "prerequisite"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "prerequisite": { "type": "string" } } }, "EditProposalInfo": { "type": "object", "required": ["page_id", "page_proposal_id", "block_proposals"], "properties": { "block_proposals": { "type": "array", "items": { "$ref": "#/components/schemas/BlockProposalInfo" } }, "page_id": { "type": "string", "format": "uuid" }, "page_proposal_id": { "type": "string", "format": "uuid" } } }, "EditedBlockNoLongerExistsData": { "type": "object", "required": ["id", "block_id", "changed_text", "original_text", "status"], "properties": { "block_id": { "type": "string", "format": "uuid" }, "changed_text": { "type": "string" }, "id": { "type": "string", "format": "uuid" }, "original_text": { "type": "string" }, "status": { "$ref": "#/components/schemas/ProposalStatus" } } }, "EditedBlockStillExistsData": { "type": "object", "required": ["id", "block_id", "current_text", "changed_text", "original_text", "status"], "properties": { "accept_preview": { "type": ["string", "null"] }, "block_id": { "type": "string", "format": "uuid" }, "changed_text": { "type": "string" }, "current_text": { "type": "string" }, "id": { "type": "string", "format": "uuid" }, "original_text": { "type": "string" }, "status": { "$ref": "#/components/schemas/ProposalStatus" } } }, "EmailData": { "type": "object", "required": ["email", "language"], "properties": { "email": { "type": "string" }, "language": { "type": "string" } } }, "EmailSendStatus": { "type": "string", "description": "What we can honestly say about an email we queued.\n\nWe only hand messages to an SMTP relay, so copy rendering this must never say \"delivered\" or\n\"received\".", "enum": ["queued", "retrying", "sent", "send_failed"] }, "EmailSendStatusReport": { "type": "object", "description": "The shared payload for every surface that reports on a queued email.", "required": ["email_send_status", "retry_count"], "properties": { "email_send_status": { "$ref": "#/components/schemas/EmailSendStatus" }, "failure_code": { "type": ["string", "null"] }, "failure_is_transient": { "type": ["boolean", "null"] }, "last_attempt_at": { "type": ["string", "null"], "format": "date-time" }, "next_retry_at": { "type": ["string", "null"], "format": "date-time" }, "retry_count": { "type": "integer", "format": "int32" }, "sent_at": { "type": ["string", "null"], "format": "date-time" } } }, "EmailTemplate": { "type": "object", "required": ["id", "created_at", "updated_at", "template_type"], "properties": { "content": {}, "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exercise_completions_threshold": { "type": ["integer", "null"], "format": "int32" }, "id": { "type": "string", "format": "uuid" }, "language": { "type": ["string", "null"] }, "points_threshold": { "type": ["integer", "null"], "format": "int32" }, "subject": { "type": ["string", "null"] }, "template_type": { "$ref": "#/components/schemas/EmailTemplateType" }, "updated_at": { "type": "string", "format": "date-time" } } }, "EmailTemplateNew": { "type": "object", "required": ["template_type"], "properties": { "content": {}, "language": { "type": ["string", "null"] }, "subject": { "type": ["string", "null"] }, "template_type": { "$ref": "#/components/schemas/EmailTemplateType" } } }, "EmailTemplateType": { "type": "string", "enum": [ "reset_password_email", "delete_user_email", "confirm_email_code", "generic", "credit_registration_account_linking", "verify_email_address", "credit_registration_action_needed", "credit_registration_registered", "credit_registration_student_number_linked" ] }, "EmailVerificationEmailInfo": { "type": "object", "description": "What we last mailed about the address the account holds now. Never a delivery confirmation: we\nhand messages to an SMTP relay and cannot see an inbox.", "required": ["sent_at", "expires_at"], "properties": { "expires_at": { "type": "string", "format": "date-time" }, "sent_at": { "type": "string", "format": "date-time" } } }, "EmailVerificationMethod": { "type": "string", "description": "How proof of control over [`UserDetail::email`] was obtained. `AdminAsserted` is the weakest.", "enum": ["emailed_code", "password_reset_backfill", "tmc_confirmed", "admin_asserted"] }, "EmailVerificationStatus": { "type": "object", "required": ["verification_enabled", "template_configured", "email"], "properties": { "email": { "type": "string" }, "email_verified_at": { "type": ["string", "null"], "format": "date-time" }, "email_verified_method": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/EmailVerificationMethod" } ] }, "latest_verification_email": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/EmailVerificationEmailInfo" } ] }, "template_configured": { "type": "boolean", "description": "False when `verification_enabled` but the deployment has no `verify_email_address`\ntemplate: requesting a code would 500, so the frontend hides the feature instead." }, "verification_enabled": { "type": "boolean", "description": "False switches the feature off entirely; the request and verify endpoints 404 then." } } }, "EventInfo": { "type": "object", "required": ["name"], "properties": { "count": { "type": ["integer", "null"], "format": "int32" }, "first_timestamp": { "type": ["string", "null"] }, "involved_object_kind": { "type": ["string", "null"] }, "involved_object_name": { "type": ["string", "null"] }, "last_timestamp": { "type": ["string", "null"] }, "message": { "type": ["string", "null"] }, "name": { "type": "string" }, "reason": { "type": ["string", "null"] }, "type_": { "type": ["string", "null"] } } }, "Exam": { "type": "object", "required": [ "id", "name", "instructions", "page_id", "courses", "time_minutes", "minimum_points_treshold", "language", "grade_manually" ], "properties": { "courses": { "type": "array", "items": { "$ref": "#/components/schemas/Course" } }, "ends_at": { "type": ["string", "null"], "format": "date-time" }, "grade_manually": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "instructions": {}, "language": { "type": "string" }, "minimum_points_treshold": { "type": "integer", "format": "int32" }, "name": { "type": "string" }, "page_id": { "type": "string", "format": "uuid" }, "starts_at": { "type": ["string", "null"], "format": "date-time" }, "time_minutes": { "type": "integer", "format": "int32" } } }, "ExamCourseInfo": { "type": "object", "required": ["course_id"], "properties": { "course_id": { "type": "string", "format": "uuid" } } }, "ExamEnrollment": { "type": "object", "required": [ "user_id", "exam_id", "started_at", "is_teacher_testing", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "ended_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": "string", "format": "uuid" }, "is_teacher_testing": { "type": "boolean" }, "show_exercise_answers": { "type": ["boolean", "null"] }, "started_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "Exercise": { "type": "object", "required": [ "id", "created_at", "updated_at", "name", "page_id", "score_maximum", "order_number", "limit_number_of_tries", "needs_peer_review", "needs_self_review", "use_course_default_peer_or_self_review_config", "teacher_reviews_answer_after_locking" ], "properties": { "chapter_id": { "type": ["string", "null"], "format": "uuid" }, "copied_from": { "type": ["string", "null"], "format": "uuid" }, "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deadline": { "type": ["string", "null"], "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "exercise_language_group_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "limit_number_of_tries": { "type": "boolean" }, "max_tries_per_slide": { "type": ["integer", "null"], "format": "int32" }, "name": { "type": "string" }, "needs_peer_review": { "type": "boolean" }, "needs_self_review": { "type": "boolean" }, "order_number": { "type": "integer", "format": "int32" }, "page_id": { "type": "string", "format": "uuid" }, "score_maximum": { "type": "integer", "format": "int32" }, "teacher_reviews_answer_after_locking": { "type": "boolean" }, "updated_at": { "type": "string", "format": "date-time" }, "use_course_default_peer_or_self_review_config": { "type": "boolean" } } }, "ExerciseAnswersInCourseRequiringAttentionCount": { "type": "object", "required": ["id", "name", "page_id", "order_number"], "properties": { "chapter_id": { "type": ["string", "null"], "format": "uuid" }, "count": { "type": ["integer", "null"], "format": "int32" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "order_number": { "type": "integer", "format": "int32" }, "page_id": { "type": "string", "format": "uuid" } } }, "ExerciseCsvExportTaskOption": { "type": "object", "required": [ "exercise_task_id", "exercise_type", "order_number", "supports_csv_export_definitions", "supports_csv_export_answers" ], "properties": { "exercise_task_id": { "type": "string", "format": "uuid" }, "exercise_type": { "type": "string" }, "order_number": { "type": "integer", "format": "int32" }, "supports_csv_export_answers": { "type": "boolean" }, "supports_csv_export_definitions": { "type": "boolean" } } }, "ExerciseRepository": { "type": "object", "required": ["id", "url", "status"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "error_message": { "type": ["string", "null"] }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "status": { "$ref": "#/components/schemas/ExerciseRepositoryStatus" }, "url": { "type": "string" } } }, "ExerciseRepositoryStatus": { "type": "string", "enum": ["Pending", "Success", "Failure"] }, "ExerciseRepositoryUpdate": { "type": "object", "required": ["url"], "properties": { "url": { "type": "string" } } }, "ExerciseResetLog": { "type": "object", "required": [ "id", "reset_for", "exercise_id", "exercise_name", "course_id", "reset_at", "created_at", "updated_at" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exercise_id": { "type": "string", "format": "uuid" }, "exercise_name": { "type": "string" }, "id": { "type": "string", "format": "uuid" }, "reason": { "type": ["string", "null"] }, "reset_at": { "type": "string", "format": "date-time" }, "reset_by": { "type": ["string", "null"], "format": "uuid" }, "reset_by_first_name": { "type": ["string", "null"] }, "reset_by_last_name": { "type": ["string", "null"] }, "reset_for": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" } } }, "ExerciseService": { "type": "object", "required": [ "id", "created_at", "updated_at", "name", "slug", "public_url", "max_reprocessing_submissions_at_once" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "internal_url": { "type": ["string", "null"], "description": "This is needed because connecting to services directly inside the cluster with a special url is much for efficient than connecting to the same service with a url that would get routed though the internet. If not defined, use we can reach the service with the public url." }, "max_reprocessing_submissions_at_once": { "type": "integer", "format": "int32" }, "name": { "type": "string" }, "public_url": { "type": "string" }, "slug": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "ExerciseServiceNewOrUpdate": { "type": "object", "required": ["name", "slug", "public_url", "max_reprocessing_submissions_at_once"], "properties": { "internal_url": { "type": ["string", "null"] }, "max_reprocessing_submissions_at_once": { "type": "integer", "format": "int32" }, "name": { "type": "string" }, "public_url": { "type": "string" }, "slug": { "type": "string" } } }, "ExerciseServiceUploadResultEntry": { "type": "object", "required": ["id", "url"], "properties": { "id": { "type": "string" }, "url": { "type": "string" } } }, "ExerciseServiceWithError": { "type": "object", "required": ["exercise_service"], "properties": { "exercise_service": { "$ref": "#/components/schemas/ExerciseService" }, "service_info_error": { "type": ["string", "null"] } } }, "ExerciseSlideSubmission": { "type": "object", "required": [ "id", "created_at", "updated_at", "exercise_slide_id", "exercise_id", "user_id", "user_points_update_strategy" ], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "exercise_id": { "type": "string", "format": "uuid" }, "exercise_slide_id": { "type": "string", "format": "uuid" }, "flag_count": { "type": ["integer", "null"], "format": "int32" }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" }, "user_points_update_strategy": { "$ref": "#/components/schemas/UserPointsUpdateStrategy" } } }, "ExerciseSlideSubmissionAndUserExerciseState": { "type": "object", "required": [ "exercise", "exercise_slide_submission", "user_exercise_state", "user_exam_enrollment" ], "properties": { "exercise": { "$ref": "#/components/schemas/Exercise" }, "exercise_slide_submission": { "$ref": "#/components/schemas/ExerciseSlideSubmission" }, "teacher_grading_decision": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TeacherGradingDecision" } ] }, "user_exam_enrollment": { "$ref": "#/components/schemas/ExamEnrollment" }, "user_exercise_state": { "$ref": "#/components/schemas/UserExerciseState" } } }, "ExerciseSlideSubmissionAndUserExerciseStateList": { "type": "object", "required": ["data", "total_pages"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmissionAndUserExerciseState" } }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 } } }, "ExerciseSlideSubmissionCount": { "type": "object", "properties": { "count": { "type": ["integer", "null"], "format": "int32" }, "date": { "type": ["string", "null"], "format": "date" } } }, "ExerciseSlideSubmissionCountByExercise": { "type": "object", "required": ["exercise_id", "exercise_name"], "properties": { "count": { "type": ["integer", "null"], "format": "int32" }, "exercise_id": { "type": "string", "format": "uuid" }, "exercise_name": { "type": "string" } } }, "ExerciseSlideSubmissionCountByWeekAndHour": { "type": "object", "properties": { "count": { "type": ["integer", "null"], "format": "int32" }, "hour": { "type": ["integer", "null"], "format": "int32" }, "isodow": { "type": ["integer", "null"], "format": "int32" } } }, "ExerciseSlideSubmissionInfo": { "type": "object", "required": ["tasks", "exercise", "exercise_slide_submission"], "properties": { "exercise": { "$ref": "#/components/schemas/Exercise" }, "exercise_slide_submission": { "$ref": "#/components/schemas/ExerciseSlideSubmission" }, "tasks": { "type": "array", "items": { "$ref": "#/components/schemas/CourseMaterialExerciseTask" } }, "user_exercise_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/UserExerciseState" } ] } } }, "ExerciseSlideSubmissionShare": { "type": "object", "description": "A shareable link to an existing exercise-slide submission. The `id` is the\nunguessable token used in the shareable URL; a viewer resolves the token back\nto the submission it points at.", "required": [ "id", "exercise_slide_submission_id", "created_by", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "created_by": { "type": "string", "format": "uuid" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exercise_slide_submission_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" } } }, "ExerciseStatusSummaryForUser": { "type": "object", "required": [ "exercise", "exercise_slide_submissions", "given_peer_or_self_review_submissions", "given_peer_or_self_review_question_submissions", "received_peer_or_self_review_submissions", "received_peer_or_self_review_question_submissions", "peer_or_self_review_questions" ], "properties": { "exercise": { "$ref": "#/components/schemas/Exercise" }, "exercise_slide_submissions": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmission" } }, "given_peer_or_self_review_question_submissions": { "type": "array", "items": { "$ref": "#/components/schemas/PeerOrSelfReviewQuestionSubmission" } }, "given_peer_or_self_review_submissions": { "type": "array", "items": { "$ref": "#/components/schemas/PeerOrSelfReviewSubmissionWithSubmissionOwner" } }, "peer_or_self_review_questions": { "type": "array", "items": { "$ref": "#/components/schemas/PeerOrSelfReviewQuestion" } }, "peer_review_queue_entry": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/PeerReviewQueueEntry" } ] }, "received_peer_or_self_review_question_submissions": { "type": "array", "items": { "$ref": "#/components/schemas/PeerOrSelfReviewQuestionSubmission" } }, "received_peer_or_self_review_submissions": { "type": "array", "items": { "$ref": "#/components/schemas/PeerOrSelfReviewSubmissionWithSubmissionOwner" } }, "teacher_grading_decision": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TeacherGradingDecision" } ] }, "user_exercise_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/UserExerciseState" } ] } } }, "ExerciseSubmissions": { "type": "object", "required": ["data", "total_pages"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/ExerciseSlideSubmission" } }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 } } }, "ExerciseTaskGrading": { "type": "object", "required": [ "id", "created_at", "updated_at", "exercise_task_submission_id", "exercise_id", "exercise_task_id", "grading_priority", "grading_progress" ], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "exercise_id": { "type": "string", "format": "uuid" }, "exercise_task_id": { "type": "string", "format": "uuid" }, "exercise_task_submission_id": { "type": "string", "format": "uuid" }, "feedback_json": {}, "feedback_text": { "type": ["string", "null"] }, "grading_completed_at": { "type": ["string", "null"], "format": "date-time" }, "grading_priority": { "type": "integer", "format": "int32" }, "grading_progress": { "$ref": "#/components/schemas/GradingProgress" }, "grading_started_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "score_given": { "type": ["number", "null"], "format": "float" }, "unscaled_score_given": { "type": ["number", "null"], "format": "float" }, "unscaled_score_maximum": { "type": ["integer", "null"], "format": "int32" }, "updated_at": { "type": "string", "format": "date-time" } } }, "ExerciseTaskGradingResult": { "type": "object", "required": ["grading_progress", "score_given", "score_maximum"], "properties": { "feedback_json": {}, "feedback_text": { "type": ["string", "null"] }, "grading_progress": { "$ref": "#/components/schemas/GradingProgress" }, "score_given": { "type": "number", "format": "float" }, "score_maximum": { "type": "integer", "format": "int32" }, "set_user_variables": { "type": ["object", "null"], "additionalProperties": {}, "propertyNames": { "type": "string" } } } }, "ExerciseTaskSubmission": { "type": "object", "required": [ "id", "created_at", "updated_at", "exercise_slide_submission_id", "exercise_task_id", "exercise_slide_id" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "data_json": {}, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exercise_slide_id": { "type": "string", "format": "uuid" }, "exercise_slide_submission_id": { "type": "string", "format": "uuid" }, "exercise_task_grading_id": { "type": ["string", "null"], "format": "uuid" }, "exercise_task_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "metadata": {}, "updated_at": { "type": "string", "format": "date-time" } } }, "ExerciseUserCounts": { "type": "object", "required": [ "exercise_name", "exercise_order_number", "page_order_number", "chapter_number", "exercise_id" ], "properties": { "chapter_number": { "type": "integer", "format": "int32" }, "exercise_id": { "type": "string", "format": "uuid" }, "exercise_name": { "type": "string" }, "exercise_order_number": { "type": "integer", "format": "int32" }, "n_users_attempted": { "type": ["integer", "null"], "format": "int64" }, "n_users_with_max_points": { "type": ["integer", "null"], "format": "int64" }, "n_users_with_some_points": { "type": ["integer", "null"], "format": "int64" }, "page_order_number": { "type": "integer", "format": "int32" } } }, "ExtendStageRequest": { "type": "object", "required": ["months"], "properties": { "months": { "type": "integer", "format": "int32", "minimum": 0 } } }, "Feedback": { "type": "object", "required": [ "id", "course_id", "feedback_given", "marked_as_read", "created_at", "blocks", "page_title", "page_url_path" ], "properties": { "blocks": { "type": "array", "items": { "$ref": "#/components/schemas/FeedbackBlock" } }, "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "feedback_given": { "type": "string" }, "id": { "type": "string", "format": "uuid" }, "marked_as_read": { "type": "boolean" }, "page_id": { "type": ["string", "null"], "format": "uuid" }, "page_title": { "type": "string" }, "page_url_path": { "type": "string" }, "selected_text": { "type": ["string", "null"] }, "user_id": { "type": ["string", "null"], "format": "uuid" } } }, "FeedbackBlock": { "type": "object", "required": ["id"], "properties": { "id": { "type": "string", "format": "uuid" }, "order_number": { "type": ["integer", "null"], "format": "int32" }, "text": { "type": ["string", "null"] } } }, "FeedbackCount": { "type": "object", "required": ["read", "unread"], "properties": { "read": { "type": "integer", "format": "int32", "minimum": 0 }, "unread": { "type": "integer", "format": "int32", "minimum": 0 } } }, "FlaggedAnswer": { "type": "object", "required": [ "id", "submission_id", "flagged_user", "flagged_by", "reason", "created_at", "updated_at" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "description": { "type": ["string", "null"] }, "flagged_by": { "type": "string", "format": "uuid" }, "flagged_user": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "reason": { "$ref": "#/components/schemas/ReportReason" }, "submission_id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" } } }, "GeneratedCertificate": { "type": "object", "required": [ "id", "created_at", "updated_at", "user_id", "name_on_certificate", "verification_id", "certificate_configuration_id" ], "properties": { "certificate_configuration_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "name_on_certificate": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" }, "verification_id": { "type": "string" } } }, "GlobalCourseModuleStatEntry": { "type": "object", "required": [ "course_name", "course_id", "course_module_id", "organization_id", "organization_name", "year", "value" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_module_ects_credits": { "type": ["number", "null"], "format": "float" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "course_name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" }, "organization_name": { "type": "string" }, "value": { "type": "integer", "format": "int64" }, "year": { "type": "string" } } }, "GlobalStatEntry": { "type": "object", "required": [ "course_name", "course_id", "organization_id", "organization_name", "year", "value" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "month": { "type": ["integer", "null"], "format": "int32" }, "organization_id": { "type": "string", "format": "uuid" }, "organization_name": { "type": "string" }, "value": { "type": "integer", "format": "int64" }, "year": { "type": "integer", "format": "int32" } } }, "GradingProgress": { "type": "string", "description": "\n\nTells what's the status of the grading progress for a user and exercise.\n\nAs close as possible LTI's grading progress for compatibility: ", "enum": ["Failed", "NotReady", "PendingManual", "Pending", "FullyGraded"] }, "GutenbergBlock": { "type": "object", "required": ["clientId", "name", "isValid", "attributes", "innerBlocks"], "properties": { "attributes": { "type": "object", "additionalProperties": {}, "propertyNames": { "type": "string" } }, "clientId": { "type": "string", "format": "uuid" }, "innerBlocks": { "type": "array", "items": { "$ref": "#/components/schemas/GutenbergBlock" } }, "isValid": { "type": "boolean" }, "name": { "type": "string" } } }, "HealthStatus": { "type": "string", "enum": ["healthy", "warning", "error"] }, "HistoryChangeReason": { "type": "string", "enum": ["PageSaved", "HistoryRestored", "PageDeleted"] }, "HistoryRestoreData": { "type": "object", "required": ["history_id"], "properties": { "history_id": { "type": "string", "format": "uuid" } } }, "IngressInfo": { "type": "object", "required": ["name", "hosts", "paths"], "properties": { "class_name": { "type": ["string", "null"] }, "hosts": { "type": "array", "items": { "type": "string" } }, "name": { "type": "string" }, "paths": { "type": "array", "items": { "type": "string" } } } }, "JobInfo": { "type": "object", "required": ["name"], "properties": { "active": { "type": ["integer", "null"], "format": "int32" }, "failed": { "type": ["integer", "null"], "format": "int32" }, "name": { "type": "string" }, "succeeded": { "type": ["integer", "null"], "format": "int32" } } }, "JoinCourseWithJoinCodePayload": { "type": "object", "required": ["join_code"], "properties": { "join_code": { "type": "string" } } }, "LegacyLedgerDivergenceRow": { "type": "object", "description": "A ledger row the legacy study-registry ledger contradicts.", "required": [ "credit_registration_id", "course_module_completion_id", "user_id", "course_id", "course_name", "course_module_id", "state", "state_entered_at", "mirror_missing", "registered_by_a_registrar" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_module_completion_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "credit_registration_id": { "type": "string", "format": "uuid" }, "email": { "type": ["string", "null"] }, "first_name": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "mirror_missing": { "type": "boolean", "description": "We registered it and the legacy ledger has no row of ours, so the teacher views and the pull\nstream still call the completion unregistered." }, "registered_by_a_registrar": { "type": "boolean", "description": "A registrar took the completion through the pull path while our pipeline had not finished\nwith it: the shape a double registration would have." }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" }, "state_entered_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "LinkingEmailStatus": { "type": "object", "description": "What we can honestly say about the linking mail: our send status, never a delivery.", "required": ["email_send_status", "emailed_to_masked"], "properties": { "email_send_status": { "$ref": "#/components/schemas/EmailSendStatus" }, "emailed_to_masked": { "type": "string" }, "sent_at": { "type": ["string", "null"], "format": "date-time" } } }, "ManualCompletionPreview": { "type": "object", "required": [ "already_completed_users", "first_time_completing_users", "non_enrolled_users" ], "properties": { "already_completed_users": { "type": "array", "items": { "$ref": "#/components/schemas/ManualCompletionPreviewUser" } }, "first_time_completing_users": { "type": "array", "items": { "$ref": "#/components/schemas/ManualCompletionPreviewUser" } }, "non_enrolled_users": { "type": "array", "items": { "$ref": "#/components/schemas/ManualCompletionPreviewUser" } } } }, "ManualCompletionPreviewUser": { "type": "object", "required": ["user_id", "passed"], "properties": { "first_name": { "type": ["string", "null"] }, "grade": { "type": ["integer", "null"], "format": "int32" }, "last_name": { "type": ["string", "null"] }, "passed": { "type": "boolean" }, "previous_best_grade": { "type": ["integer", "null"], "format": "int32" }, "user_id": { "type": "string", "format": "uuid" } } }, "MarkAsRead": { "type": "object", "required": ["read"], "properties": { "read": { "type": "boolean" } } }, "MaterialReference": { "type": "object", "required": ["id", "course_id", "citation_key", "reference", "created_at", "updated_at"], "properties": { "citation_key": { "type": "string" }, "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "reference": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "ModelType": { "type": "string", "enum": ["GPTThinking", "GPTNonThinking", "GPTHardThinking", "Mistral"] }, "ModifiedModule": { "type": "object", "required": [ "id", "order_number", "completion_policy", "enable_registering_completion_to_uh_open_university", "enable_credit_registration_via_suotar", "credit_registration" ], "properties": { "completion_policy": { "$ref": "#/components/schemas/CompletionPolicy" }, "completion_registration_link_override": { "type": ["string", "null"] }, "credit_registration": { "$ref": "#/components/schemas/CourseModuleCreditRegistrationEdit" }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "enable_credit_registration_via_suotar": { "type": "boolean" }, "enable_registering_completion_to_uh_open_university": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": ["string", "null"] }, "order_number": { "type": "integer", "format": "int32" }, "uh_course_code": { "type": ["string", "null"] } } }, "Module": { "type": "object", "required": ["course_code", "description", "prerequisites"], "properties": { "course_code": { "type": "string" }, "description": { "type": "string" }, "prerequisites": { "type": "array", "items": { "type": "string" } } } }, "ModuleUpdates": { "type": "object", "required": ["new_modules", "deleted_modules", "modified_modules", "moved_chapters"], "properties": { "deleted_modules": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "modified_modules": { "type": "array", "items": { "$ref": "#/components/schemas/ModifiedModule" } }, "moved_chapters": { "type": "array", "items": { "type": "array", "items": false, "prefixItems": [ { "type": "string", "format": "uuid" }, { "type": "string", "format": "uuid" } ] } }, "new_modules": { "type": "array", "items": { "$ref": "#/components/schemas/NewModule" } } } }, "MyCourse": { "allOf": [ { "$ref": "#/components/schemas/Course" }, { "type": "object", "required": ["can_hide"], "properties": { "can_hide": { "type": "boolean", "description": "Whether the course can be hidden from the \"My courses\" list. False for courses the user has\nnot enrolled in or has a role in." } } } ] }, "MyCourseCreditRegistrationConsent": { "type": "object", "required": [ "course_id", "course_name", "asked", "credit_registration_enabled_for_course", "modules", "registrable_completion_count" ], "properties": { "asked": { "type": "boolean", "description": "False means never asked, which is not the same as asked and declined." }, "consent_given": { "type": ["boolean", "null"] }, "consent_given_at": { "type": ["string", "null"], "format": "date-time" }, "consent_withdrawn_at": { "type": ["string", "null"], "format": "date-time" }, "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "credit_registration_enabled_for_course": { "type": "boolean" }, "modules": { "type": "array", "items": { "$ref": "#/components/schemas/CreditRegistrationConsentModule" } }, "registrable_completion_count": { "type": "integer", "format": "int64", "description": "Completions already waiting on consent, so the dialog can say how many one click registers." } } }, "MyCreditRegistration": { "type": "object", "required": [ "id", "course_id", "course_name", "course_slug", "course_module_id", "completion_date", "student_facing_status", "consent_withdrawn_while_in_flight", "registry_already_held_equal_or_better", "status_is_moving", "next_attempt_at", "attempt_number", "superseded", "can_request_enrolment_recheck" ], "properties": { "attempt_number": { "type": "integer", "format": "int32" }, "can_request_enrolment_recheck": { "type": "boolean" }, "completion_date": { "type": "string", "format": "date-time" }, "consent_withdrawn_while_in_flight": { "type": "boolean", "description": "The one thing the page needs beyond the status: the outcome of an import that was already in\nflight is unknown, so it gets its own copy.\n\nThe ledger state itself is deliberately not on the wire. Eligibility includes\n`NOT needs_to_be_reviewed`, so `blocked` would tell a student they have been flagged as a\nsuspected cheater — which `users.rs` hides from them for that exact reason. Every cause of\n`not_registering` has to stay indistinguishable here." }, "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "course_name": { "type": "string" }, "course_slug": { "type": "string" }, "credits": { "type": ["number", "null"], "format": "float" }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "enrolment_link": { "type": ["string", "null"], "description": "The open university enrolment page, for a row the study registry has no enrolment for." }, "enrolment_realisation_name": { "type": ["string", "null"] }, "error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "grade_id": { "type": ["string", "null"] }, "grade_scale_id": { "type": ["string", "null"], "description": "Names the scale `grade_id` is on, without which \"1\" reads as a one out of five when it means\na pass." }, "id": { "type": "string", "format": "uuid" }, "linking_email": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/LinkingEmailStatus", "description": "Only on a row waiting for a student number whose account was linked at some point: the mail is\naddressed to a Sisu person, and a never-linked account names none." } ] }, "next_attempt_at": { "type": "string", "format": "date-time" }, "notification_email": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/NotificationEmailStatus", "description": "The terminal-state mail this row's status has, if one has been queued." } ] }, "registered_at": { "type": ["string", "null"], "format": "date-time" }, "registry_already_held_equal_or_better": { "type": "boolean", "description": "The registry declined this attempt because it already holds an equal or better grade. Still a\n`registered` stage — the credit exists — but the student raised a grade and nothing changed,\nso it earns a line of its own. Unlike the ledger state, this one is safe to expose: it says\nsomething about the student's own transcript and nothing about how we treat them." }, "sisu_attainment_id": { "type": ["string", "null"] }, "status_is_moving": { "type": "boolean", "description": "Whether the pipeline is still expected to move this row: drives the status page's polling." }, "student_facing_status": { "$ref": "#/components/schemas/StudentFacingCreditRegistrationStatus" }, "superseded": { "type": "boolean" }, "uh_course_code": { "type": ["string", "null"] } } }, "MyCreditRegistrationConsent": { "type": "object", "required": [ "course_id", "course_name", "registrable_completion_count", "registered_count" ], "properties": { "asked_at": { "type": ["string", "null"], "format": "date-time" }, "consent_given": { "type": ["boolean", "null"] }, "consent_given_at": { "type": ["string", "null"], "format": "date-time" }, "consent_withdrawn_at": { "type": ["string", "null"], "format": "date-time" }, "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "registered_count": { "type": "integer", "format": "int64" }, "registrable_completion_count": { "type": "integer", "format": "int64" } } }, "MyCreditRegistrationForCourseModule": { "type": "object", "description": "The live registration for one course module, with the attempts a newer one replaced.", "required": ["registration", "earlier_attempts"], "properties": { "earlier_attempts": { "type": "array", "items": { "$ref": "#/components/schemas/MyCreditRegistration" }, "description": "The module's other rows, newest completion first. Shown because the study registry may hold an\nearlier attempt's attainment as well as the current one's." }, "registration": { "$ref": "#/components/schemas/MyCreditRegistration" } } }, "MyStudies": { "type": "object", "required": ["any_module_supports_credit_registration", "courses", "totals"], "properties": { "any_module_supports_credit_registration": { "type": "boolean", "description": "Drives whether the profile's credit-registration tab renders. Covers hidden courses too:\nhiding a course must not take away access to registering its credits." }, "courses": { "type": "array", "items": { "$ref": "#/components/schemas/MyStudiesCourse" } }, "totals": { "$ref": "#/components/schemas/MyStudiesTotals" } } }, "MyStudiesCompletion": { "type": "object", "description": "A completion as the student may see it. `needs_to_be_reviewed` ones are excluded so a student\ncannot infer that they are under suspicion.", "required": [ "course_module_completion_id", "completion_date", "passed", "prerequisite_modules_completed" ], "properties": { "completion_date": { "type": "string", "format": "date-time" }, "course_module_completion_id": { "type": "string", "format": "uuid" }, "grade": { "type": ["integer", "null"], "format": "int32", "description": "`None` on pass/fail modules; the frontend falls back to `passed`." }, "passed": { "type": "boolean" }, "prerequisite_modules_completed": { "type": "boolean" } } }, "MyStudiesCourse": { "type": "object", "required": [ "course_id", "course_name", "course_slug", "organization_slug", "language_code", "first_enrolled_at", "is_current", "hidden", "supports_credit_registration", "modules" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "course_slug": { "type": "string" }, "current_course_instance_id": { "type": ["string", "null"], "format": "uuid", "description": "The instance the per-module progress is fetched for. `None` if the enrolment has no instance." }, "current_course_instance_name": { "type": ["string", "null"] }, "first_enrolled_at": { "type": "string", "format": "date-time" }, "hidden": { "type": "boolean", "description": "Hidden courses are included here, unlike in `getMyCourses`, so the profile can offer unhiding." }, "is_current": { "type": "boolean", "description": "False when the student's active version of this course is a different language version." }, "language_code": { "type": "string" }, "modules": { "type": "array", "items": { "$ref": "#/components/schemas/MyStudiesCourseModule" } }, "organization_slug": { "type": "string" }, "supports_credit_registration": { "type": "boolean" } } }, "MyStudiesCourseModule": { "type": "object", "description": "A course module as the student's own profile shows it, with their best visible completion.", "required": ["course_module_id", "order_number", "supports_credit_registration"], "properties": { "completion": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/MyStudiesCompletion", "description": "`None` when no completion may be shown to the student. May be a failed one, so check `passed`." } ] }, "course_module_id": { "type": "string", "format": "uuid" }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "name": { "type": ["string", "null"], "description": "`None` for the course's default module; the frontend labels those with the course name." }, "order_number": { "type": "integer", "format": "int32" }, "supports_credit_registration": { "type": "boolean" }, "uh_course_code": { "type": ["string", "null"] } } }, "MyStudiesTotals": { "type": "object", "description": "Summarises the courses the profile lists, i.e. the non-hidden ones.", "required": ["courses", "completions", "ects"], "properties": { "completions": { "type": "integer", "format": "int32", "description": "Counts passed completions only." }, "courses": { "type": "integer", "format": "int32" }, "ects": { "type": "number", "format": "float", "description": "Summed over passed completions only." } } }, "MyVerifiedStudentNumber": { "type": "object", "description": "The account's linked student number, unmasked: it is the holder's own.", "required": [ "student_number", "verified_at", "verified_via", "linked_automatically", "auto_link_notice_dismissed" ], "properties": { "auto_link_notice_dismissed": { "type": "boolean" }, "first_names": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "linked_automatically": { "type": "boolean", "description": "Whether the pipeline linked this without asking, because the study registry holds this\naccount's verified address for the student number. True until the student puts the notice\naway, and the notice is what makes a wrong automatic link noticeable." }, "student_number": { "type": "string" }, "verified_at": { "type": "string", "format": "date-time" }, "verified_via": { "$ref": "#/components/schemas/StudentNumberVerificationMethod" }, "verified_via_email_masked": { "type": ["string", "null"], "description": "The Sisu-held address the proof rests on, masked; `None` when support linked it by hand." } } }, "NeverEnteredCompletion": { "type": "object", "description": "A completion that satisfies the materialise predicate and has no ledger row.", "required": [ "course_module_completion_id", "user_id", "course_id", "course_name", "course_module_id", "completion_date", "created_at", "missing_enrolment" ], "properties": { "completion_date": { "type": "string", "format": "date-time" }, "course_id": { "type": "string", "format": "uuid" }, "course_module_completion_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "course_name": { "type": "string" }, "created_at": { "type": "string", "format": "date-time" }, "email": { "type": ["string", "null"] }, "first_name": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "missing_enrolment": { "type": "boolean", "description": "The student has no enrolment on the course, so `materialize` has no course instance to put\non a ledger row. The one cause running the phase again will not fix." }, "user_id": { "type": "string", "format": "uuid" } } }, "NewChapter": { "type": "object", "required": ["name", "course_id", "chapter_number"], "properties": { "chapter_number": { "type": "integer", "format": "int32" }, "color": { "type": ["string", "null"] }, "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": ["string", "null"], "format": "uuid", "description": "If undefined when creating a chapter, will use the course default one.\nCHANGE TO NON NULL WHEN FRONTEND MODULE EDITING IMPLEMENTED" }, "deadline": { "type": ["string", "null"], "format": "date-time" }, "front_page_id": { "type": ["string", "null"], "format": "uuid" }, "name": { "type": "string" }, "opens_at": { "type": ["string", "null"], "format": "date-time" } } }, "NewChatbotConf": { "type": "object", "required": [ "enabled_to_students", "chatbot_name", "model_id", "prompt", "initial_message", "weekly_tokens_per_user", "daily_tokens_per_user", "temperature", "top_p", "frequency_penalty", "presence_penalty", "max_output_tokens", "verbosity", "reasoning_effort", "use_azure_search", "maintain_azure_search_index", "hide_citations", "use_semantic_reranking", "enabled_tool_categories", "default_chatbot", "suggest_next_messages", "publicly_accessible" ], "properties": { "chatbot_name": { "type": "string" }, "chatbotconf_id": { "type": ["string", "null"], "format": "uuid" }, "course_id": { "type": ["string", "null"], "format": "uuid" }, "daily_tokens_per_user": { "type": "integer", "format": "int32" }, "default_chatbot": { "type": "boolean" }, "enabled_to_students": { "type": "boolean" }, "enabled_tool_categories": { "type": "array", "items": { "$ref": "#/components/schemas/ToolCategory" } }, "frequency_penalty": { "type": "number", "format": "float" }, "hide_citations": { "type": "boolean" }, "initial_message": { "type": "string" }, "initial_suggested_messages": { "type": ["array", "null"], "items": { "type": "string" } }, "maintain_azure_search_index": { "type": "boolean" }, "max_output_tokens": { "type": "integer", "format": "int32" }, "model_id": { "type": "string", "format": "uuid" }, "presence_penalty": { "type": "number", "format": "float" }, "prompt": { "type": "string" }, "publicly_accessible": { "type": "boolean" }, "reasoning_effort": { "$ref": "#/components/schemas/ReasoningEffortLevel" }, "suggest_next_messages": { "type": "boolean" }, "temperature": { "type": "number", "format": "float" }, "top_p": { "type": "number", "format": "float" }, "use_azure_search": { "type": "boolean" }, "use_semantic_reranking": { "type": "boolean" }, "verbosity": { "$ref": "#/components/schemas/VerbosityLevel" }, "weekly_tokens_per_user": { "type": "integer", "format": "int32" } } }, "NewCodeGiveaway": { "type": "object", "required": ["course_id", "name"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": ["string", "null"], "format": "uuid" }, "name": { "type": "string" }, "require_course_specific_consent_form_question_id": { "type": ["string", "null"], "format": "uuid" } } }, "NewCourse": { "type": "object", "description": "Represents the subset of page fields that are required to create a new course.", "required": [ "name", "slug", "organization_id", "language_code", "teacher_in_charge_name", "teacher_in_charge_email", "description", "is_draft", "is_test_mode", "is_unlisted", "copy_user_permissions", "is_joinable_by_code_only", "ask_marketing_consent", "can_add_chatbot" ], "properties": { "ask_marketing_consent": { "type": "boolean" }, "can_add_chatbot": { "type": "boolean" }, "copy_user_permissions": { "type": "boolean", "description": "If true, copies all user permissions from the original course to the new one." }, "description": { "type": "string" }, "flagged_answers_threshold": { "type": ["integer", "null"], "format": "int32" }, "is_draft": { "type": "boolean" }, "is_joinable_by_code_only": { "type": "boolean" }, "is_test_mode": { "type": "boolean" }, "is_unlisted": { "type": "boolean" }, "join_code": { "type": ["string", "null"] }, "language_code": { "type": "string" }, "name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" }, "slug": { "type": "string" }, "teacher_in_charge_email": { "type": "string", "description": "Email of the teacher who is responsible for the course. Must be a valid email." }, "teacher_in_charge_name": { "type": "string", "description": "Name of the teacher who is responsible for the course. Must be a valid name." } } }, "NewExam": { "type": "object", "required": [ "name", "time_minutes", "organization_id", "minimum_points_treshold", "grade_manually" ], "properties": { "ends_at": { "type": ["string", "null"], "format": "date-time" }, "grade_manually": { "type": "boolean" }, "minimum_points_treshold": { "type": "integer", "format": "int32" }, "name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" }, "starts_at": { "type": ["string", "null"], "format": "date-time" }, "time_minutes": { "type": "integer", "format": "int32" } } }, "NewExerciseRepository": { "type": "object", "required": ["git_url"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "deploy_key": { "type": ["string", "null"] }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "git_url": { "type": "string" }, "public_key": { "type": ["string", "null"] } } }, "NewMaterialReference": { "type": "object", "required": ["citation_key", "reference"], "properties": { "citation_key": { "type": "string" }, "reference": { "type": "string" } } }, "NewModule": { "type": "object", "required": [ "name", "order_number", "chapters", "completion_policy", "enable_registering_completion_to_uh_open_university", "enable_credit_registration_via_suotar", "credit_registration" ], "properties": { "chapters": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "completion_policy": { "$ref": "#/components/schemas/CompletionPolicy" }, "completion_registration_link_override": { "type": ["string", "null"] }, "credit_registration": { "$ref": "#/components/schemas/CourseModuleCreditRegistrationEdit" }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "enable_credit_registration_via_suotar": { "type": "boolean" }, "enable_registering_completion_to_uh_open_university": { "type": "boolean" }, "name": { "type": "string" }, "order_number": { "type": "integer", "format": "int32" }, "uh_course_code": { "type": ["string", "null"] } } }, "NewPage": { "type": "object", "description": "Represents the subset of page fields that are required to create a new page.", "required": [ "exercises", "exercise_slides", "exercise_tasks", "content", "url_path", "title", "hidden" ], "properties": { "chapter_id": { "type": ["string", "null"], "format": "uuid" }, "content": { "type": "array", "items": { "$ref": "#/components/schemas/GutenbergBlock" } }, "content_search_language": { "type": ["string", "null"], "description": "Read from the course's settings if None. If course_id is None as well, defaults to \"simple\"" }, "course_id": { "type": ["string", "null"], "format": "uuid" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "exercise_slides": { "type": "array", "items": { "$ref": "#/components/schemas/CmsPageExerciseSlide" } }, "exercise_tasks": { "type": "array", "items": { "$ref": "#/components/schemas/CmsPageExerciseTask" } }, "exercises": { "type": "array", "items": { "$ref": "#/components/schemas/CmsPageExercise" } }, "front_page_of_chapter_id": { "type": ["string", "null"], "format": "uuid", "description": "If set, set this page to be the front page of this course part." }, "hidden": { "type": "boolean" }, "title": { "type": "string" }, "url_path": { "type": "string" } } }, "NewRegrading": { "type": "object", "required": ["user_points_update_strategy", "ids", "id_type"], "properties": { "id_type": { "$ref": "#/components/schemas/NewRegradingIdType" }, "ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "user_points_update_strategy": { "$ref": "#/components/schemas/UserPointsUpdateStrategy" } } }, "NewRegradingIdType": { "type": "string", "enum": ["ExerciseTaskSubmissionId", "ExerciseId"] }, "NewTeacherGradingDecision": { "type": "object", "required": ["user_exercise_state_id", "exercise_id", "action", "hidden", "reset_exercise"], "properties": { "action": { "$ref": "#/components/schemas/TeacherDecisionType" }, "exercise_id": { "type": "string", "format": "uuid" }, "hidden": { "type": "boolean" }, "justification": { "type": ["string", "null"] }, "manual_points": { "type": ["number", "null"], "format": "float" }, "reset_exercise": { "type": "boolean", "description": "Resets the student's progress on the exercise so they can answer it again. Independent of\n`action`, so a decision can record why the answer was rejected and still reopen it.\nRequires the exercise to belong to a course." }, "user_exercise_state_id": { "type": "string", "format": "uuid" } } }, "NotImprovedAttainment": { "type": "object", "description": "The attainment the study registry pointed at when it turned a submission down as no improvement.\n\nRead back off the event the answer was recorded on rather than stored on the ledger row: the row\nholds what we sent, and this is what the registry already had.", "properties": { "grade_id": { "type": ["string", "null"] }, "grade_scale_id": { "type": ["string", "null"], "description": "Names the scale `grade_id` is on, without which \"1\" reads as a one out of five when it means\na pass." } } }, "NotificationEmailStatus": { "type": "object", "description": "The same, for one of the two terminal-state mails. No address: these go to the account's own,\nwhich the reader either owns or already sees.", "required": ["kind", "email_send_status"], "properties": { "email_send_status": { "$ref": "#/components/schemas/EmailSendStatus" }, "kind": { "$ref": "#/components/schemas/CreditRegistrationNotificationKind" }, "sent_at": { "type": ["string", "null"], "format": "date-time" } } }, "OrgExam": { "type": "object", "required": [ "id", "created_at", "updated_at", "name", "instructions", "time_minutes", "organization_id", "minimum_points_treshold", "grade_manually" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "ends_at": { "type": ["string", "null"], "format": "date-time" }, "grade_manually": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "instructions": {}, "language": { "type": ["string", "null"] }, "minimum_points_treshold": { "type": "integer", "format": "int32" }, "name": { "type": "string" }, "organization_id": { "type": "string", "format": "uuid" }, "starts_at": { "type": ["string", "null"], "format": "date-time" }, "time_minutes": { "type": "integer", "format": "int32" }, "updated_at": { "type": "string", "format": "date-time" } } }, "Organization": { "type": "object", "required": ["id", "slug", "created_at", "updated_at", "name", "hidden"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "description": { "type": ["string", "null"] }, "hidden": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "organization_image_url": { "type": ["string", "null"] }, "slug": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "OrganizationCreatePayload": { "type": "object", "required": ["name", "slug", "hidden"], "properties": { "hidden": { "type": "boolean" }, "name": { "type": "string" }, "slug": { "type": "string" } } }, "OrganizationUpdatePayload": { "type": "object", "required": ["name", "hidden", "slug"], "properties": { "hidden": { "type": "boolean" }, "name": { "type": "string" }, "slug": { "type": "string" } } }, "Page": { "type": "object", "required": [ "id", "created_at", "updated_at", "url_path", "title", "content", "order_number", "hidden" ], "properties": { "chapter_id": { "type": ["string", "null"], "format": "uuid" }, "content": {}, "copied_from": { "type": ["string", "null"], "format": "uuid" }, "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "hidden": { "type": "boolean" }, "id": { "type": "string", "format": "uuid" }, "order_number": { "type": "integer", "format": "int32" }, "page_language_group_id": { "type": ["string", "null"], "format": "uuid" }, "title": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" }, "url_path": { "type": "string" } } }, "PageAudioFile": { "type": "object", "required": ["id", "page_id", "created_at", "path", "mime_type"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "mime_type": { "type": "string" }, "page_id": { "type": "string", "format": "uuid" }, "path": { "type": "string" } } }, "PageDetailsUpdate": { "type": "object", "description": "Represents the subset of page fields that can be updated from the main frontend dialog \"Edit page details\".", "required": ["title", "url_path"], "properties": { "title": { "type": "string" }, "url_path": { "type": "string" } } }, "PageHistory": { "type": "object", "required": [ "id", "created_at", "updated_at", "title", "content", "history_change_reason", "author_user_id", "page_id" ], "properties": { "author_user_id": { "type": "string", "format": "uuid" }, "content": {}, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "history_change_reason": { "$ref": "#/components/schemas/HistoryChangeReason" }, "id": { "type": "string", "format": "uuid" }, "page_id": { "type": "string", "format": "uuid" }, "restored_from_id": { "type": ["string", "null"], "format": "uuid" }, "title": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "PageInfo": { "type": "object", "required": ["page_id", "page_title"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "course_name": { "type": ["string", "null"] }, "course_slug": { "type": ["string", "null"] }, "organization_slug": { "type": ["string", "null"] }, "page_id": { "type": "string", "format": "uuid" }, "page_title": { "type": "string" } } }, "PageProposal": { "type": "object", "required": [ "id", "page_id", "pending", "created_at", "block_proposals", "page_title", "page_url_path" ], "properties": { "block_proposals": { "type": "array", "items": { "$ref": "#/components/schemas/BlockProposal" } }, "created_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "page_id": { "type": "string", "format": "uuid" }, "page_title": { "type": "string" }, "page_url_path": { "type": "string" }, "pending": { "type": "boolean" }, "user_id": { "type": ["string", "null"], "format": "uuid" } } }, "PageVisitDatumSummaryByCourse": { "type": "object", "required": ["id", "created_at", "updated_at", "num_visitors", "visit_date"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "num_visitors": { "type": "integer", "format": "int32" }, "referrer": { "type": ["string", "null"] }, "updated_at": { "type": "string", "format": "date-time" }, "utm_campaign": { "type": ["string", "null"] }, "utm_content": { "type": ["string", "null"] }, "utm_medium": { "type": ["string", "null"] }, "utm_source": { "type": ["string", "null"] }, "utm_term": { "type": ["string", "null"] }, "visit_date": { "type": "string", "format": "date" } } }, "PageVisitDatumSummaryByCourseDeviceTypes": { "type": "object", "required": ["id", "created_at", "updated_at", "num_visitors", "visit_date"], "properties": { "browser": { "type": ["string", "null"] }, "browser_version": { "type": ["string", "null"] }, "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "device_type": { "type": ["string", "null"] }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "num_visitors": { "type": "integer", "format": "int32" }, "operating_system": { "type": ["string", "null"] }, "updated_at": { "type": "string", "format": "date-time" }, "visit_date": { "type": "string", "format": "date" } } }, "PageVisitDatumSummaryByCoursesCountries": { "type": "object", "required": ["id", "created_at", "updated_at", "num_visitors", "visit_date"], "properties": { "country": { "type": ["string", "null"] }, "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "num_visitors": { "type": "integer", "format": "int32" }, "updated_at": { "type": "string", "format": "date-time" }, "visit_date": { "type": "string", "format": "date" } } }, "PageVisitDatumSummaryByPages": { "type": "object", "required": ["id", "created_at", "updated_at", "page_id", "num_visitors", "visit_date"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "num_visitors": { "type": "integer", "format": "int32" }, "page_id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "visit_date": { "type": "string", "format": "date" } } }, "PaperSize": { "type": "string", "enum": ["horizontal-a4", "vertical-a4"] }, "PeerOrSelfReviewAnswer": { "oneOf": [ { "type": "object", "required": ["type"], "properties": { "type": { "type": "string", "enum": ["no-answer"] } } }, { "type": "object", "required": ["value", "type"], "properties": { "type": { "type": "string", "enum": ["essay"] }, "value": { "type": "string" } } }, { "type": "object", "required": ["value", "type"], "properties": { "type": { "type": "string", "enum": ["scale"] }, "value": { "type": "number", "format": "float" } } } ] }, "PeerOrSelfReviewQuestion": { "type": "object", "required": [ "id", "created_at", "updated_at", "peer_or_self_review_config_id", "order_number", "question", "question_type", "answer_required", "weight" ], "properties": { "answer_required": { "type": "boolean" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "order_number": { "type": "integer", "format": "int32" }, "peer_or_self_review_config_id": { "type": "string", "format": "uuid" }, "question": { "type": "string" }, "question_type": { "$ref": "#/components/schemas/PeerOrSelfReviewQuestionType" }, "updated_at": { "type": "string", "format": "date-time" }, "weight": { "type": "number", "format": "float" } } }, "PeerOrSelfReviewQuestionAndAnswer": { "type": "object", "required": [ "peer_or_self_review_config_id", "peer_or_self_review_question_id", "peer_or_self_review_submission_id", "peer_review_question_submission_id", "order_number", "question", "answer", "answer_required" ], "properties": { "answer": { "$ref": "#/components/schemas/PeerOrSelfReviewAnswer" }, "answer_required": { "type": "boolean" }, "order_number": { "type": "integer", "format": "int32" }, "peer_or_self_review_config_id": { "type": "string", "format": "uuid" }, "peer_or_self_review_question_id": { "type": "string", "format": "uuid" }, "peer_or_self_review_submission_id": { "type": "string", "format": "uuid" }, "peer_review_question_submission_id": { "type": "string", "format": "uuid" }, "question": { "type": "string" } } }, "PeerOrSelfReviewQuestionSubmission": { "type": "object", "required": [ "id", "created_at", "updated_at", "peer_or_self_review_question_id", "peer_or_self_review_submission_id" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "number_data": { "type": ["number", "null"], "format": "float" }, "peer_or_self_review_question_id": { "type": "string", "format": "uuid" }, "peer_or_self_review_submission_id": { "type": "string", "format": "uuid" }, "text_data": { "type": ["string", "null"] }, "updated_at": { "type": "string", "format": "date-time" } } }, "PeerOrSelfReviewQuestionType": { "type": "string", "enum": ["Essay", "Scale"] }, "PeerOrSelfReviewSubmission": { "type": "object", "required": [ "id", "created_at", "updated_at", "user_id", "exercise_id", "course_id", "peer_or_self_review_config_id", "exercise_slide_submission_id" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exercise_id": { "type": "string", "format": "uuid" }, "exercise_slide_submission_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "peer_or_self_review_config_id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "PeerOrSelfReviewSubmissionWithSubmissionOwner": { "allOf": [ { "$ref": "#/components/schemas/PeerOrSelfReviewSubmission" }, { "type": "object", "properties": { "submission_owner_user_id": { "type": ["string", "null"], "format": "uuid" } } } ], "description": "Same as PeerOrSelfReviewSubmission with optional submission owner (user who received the review). Used for \"given\" reviews." }, "PeerReviewProcessingStrategy": { "type": "string", "description": "\nDetermines how we will treat the answer being peer reviewed once it has received enough reviews and the student has given enough peer reviews.\n\nSome strategies compare the overall received peer review likert answer (1-5) average to peer_reviews.accepting threshold.", "enum": [ "AutomaticallyGradeByAverage", "AutomaticallyGradeOrManualReviewByAverage", "ManualReviewEverything" ] }, "PeerReviewQueueEntry": { "type": "object", "required": [ "id", "created_at", "updated_at", "user_id", "exercise_id", "course_id", "receiving_peer_reviews_exercise_slide_submission_id", "received_enough_peer_reviews", "peer_review_priority", "removed_from_queue_for_unusual_reason" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exercise_id": { "type": "string", "format": "uuid" }, "id": { "type": "string", "format": "uuid" }, "peer_review_priority": { "type": "integer", "format": "int32" }, "received_enough_peer_reviews": { "type": "boolean" }, "receiving_peer_reviews_exercise_slide_submission_id": { "type": "string", "format": "uuid" }, "removed_from_queue_for_unusual_reason": { "type": "boolean" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "PeerReviewWithQuestionsAndAnswers": { "type": "object", "required": [ "peer_or_self_review_submission_id", "peer_review_giver_user_id", "questions_and_answers" ], "properties": { "peer_or_self_review_submission_id": { "type": "string", "format": "uuid" }, "peer_review_giver_user_id": { "type": "string", "format": "uuid" }, "questions_and_answers": { "type": "array", "items": { "$ref": "#/components/schemas/PeerOrSelfReviewQuestionAndAnswer" } } } }, "PendingRole": { "type": "object", "required": ["id", "created_at", "updated_at", "user_email", "role", "expires_at"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "course_instance_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "expires_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "role": { "$ref": "#/components/schemas/UserRole" }, "updated_at": { "type": "string", "format": "date-time" }, "user_email": { "type": "string" } } }, "PlanMemberWithDetails": { "type": "object", "required": ["id", "user_id", "email", "created_at"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "email": { "type": "string", "format": "email" }, "first_name": { "type": ["string", "null"] }, "id": { "type": "string", "format": "uuid" }, "last_name": { "type": ["string", "null"] }, "user_id": { "type": "string", "format": "uuid" } } }, "PlaygroundExample": { "type": "object", "required": ["id", "created_at", "updated_at", "name", "url", "width", "data"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "data": {}, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" }, "url": { "type": "string" }, "width": { "type": "integer", "format": "int32" } } }, "PlaygroundExampleData": { "type": "object", "required": ["name", "url", "width", "data"], "properties": { "data": {}, "name": { "type": "string" }, "url": { "type": "string" }, "width": { "type": "integer", "format": "int32" } } }, "PodDisruptionBudgetInfo": { "type": "object", "required": [ "name", "current_healthy", "desired_healthy", "disruptions_allowed", "expected_pods", "selector_labels" ], "properties": { "current_healthy": { "type": "integer", "format": "int32" }, "desired_healthy": { "type": "integer", "format": "int32" }, "disruptions_allowed": { "type": "integer", "format": "int32" }, "expected_pods": { "type": "integer", "format": "int32" }, "name": { "type": "string" }, "selector_labels": { "type": "object", "additionalProperties": { "type": "string" }, "propertyNames": { "type": "string" } } } }, "PodInfo": { "type": "object", "required": ["name", "phase", "labels"], "properties": { "labels": { "type": "object", "additionalProperties": { "type": "string" }, "propertyNames": { "type": "string" } }, "name": { "type": "string" }, "phase": { "type": "string" }, "ready": { "type": ["boolean", "null"] } } }, "PointMap": { "type": "object", "additionalProperties": { "type": "number", "format": "float" }, "propertyNames": { "type": "string", "format": "uuid" } }, "Points": { "type": "object", "required": ["chapter_points", "users", "user_chapter_points"], "properties": { "chapter_points": { "type": "array", "items": { "$ref": "#/components/schemas/ChapterScore" } }, "user_chapter_points": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/PointMap" }, "propertyNames": { "type": "string", "format": "uuid" } }, "users": { "type": "array", "items": { "$ref": "#/components/schemas/UserDetail" } } } }, "ProposalCount": { "type": "object", "required": ["pending", "handled"], "properties": { "handled": { "type": "integer", "format": "int32", "minimum": 0 }, "pending": { "type": "integer", "format": "int32", "minimum": 0 } } }, "ProposalStatus": { "type": "string", "enum": ["Pending", "Accepted", "Rejected"] }, "ReasoningEffortLevel": { "type": "string", "enum": ["none", "minimal", "low", "medium", "high", "xhigh"] }, "ReconciliationRegistration": { "type": "object", "description": "One ledger row, flattened to what every reconciliation list needs to name a student and link\nonwards.", "required": [ "credit_registration_id", "user_id", "course_id", "course_name", "course_module_id", "state", "state_entered_at" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"] }, "course_name": { "type": "string" }, "credit_registration_id": { "type": "string", "format": "uuid" }, "email": { "type": ["string", "null"] }, "first_name": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "registered_at": { "type": ["string", "null"], "format": "date-time" }, "sisu_attainment_id": { "type": ["string", "null"] }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" }, "state_entered_at": { "type": "string", "format": "date-time" }, "student_number": { "type": ["string", "null"] }, "submitted_at": { "type": ["string", "null"], "format": "date-time" }, "submitted_attainment_id": { "type": ["string", "null"] }, "terminal_at": { "type": ["string", "null"], "format": "date-time" }, "uh_course_code": { "type": ["string", "null"] }, "user_id": { "type": "string", "format": "uuid" } } }, "Regrading": { "type": "object", "required": [ "id", "created_at", "updated_at", "total_grading_progress", "user_points_update_strategy" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "error_message": { "type": ["string", "null"] }, "id": { "type": "string", "format": "uuid" }, "regrading_completed_at": { "type": ["string", "null"], "format": "date-time" }, "regrading_started_at": { "type": ["string", "null"], "format": "date-time" }, "total_grading_progress": { "$ref": "#/components/schemas/GradingProgress" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": ["string", "null"], "format": "uuid" }, "user_points_update_strategy": { "$ref": "#/components/schemas/UserPointsUpdateStrategy" } } }, "RegradingInfo": { "type": "object", "required": ["regrading", "submission_infos"], "properties": { "regrading": { "$ref": "#/components/schemas/Regrading" }, "submission_infos": { "type": "array", "items": { "$ref": "#/components/schemas/RegradingSubmissionInfo" } } } }, "RegradingSubmissionInfo": { "type": "object", "required": ["exercise_task_submission_id", "grading_before_regrading"], "properties": { "exercise_task_submission_id": { "type": "string", "format": "uuid" }, "grading_after_regrading": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/ExerciseTaskGrading" } ] }, "grading_before_regrading": { "$ref": "#/components/schemas/ExerciseTaskGrading" } } }, "ReportReason": { "type": "string", "enum": ["Spam", "HarmfulContent", "AiGenerated"] }, "RequestCreditRegistrationEnrolmentRecheckResult": { "type": "object", "required": ["recheck_started"], "properties": { "next_recheck_allowed_at": { "type": ["string", "null"], "format": "date-time" }, "recheck_started": { "type": "boolean", "description": "False when we looked so recently that asking again would tell the student nothing new." } } }, "RequestEmailVerificationOutcome": { "type": "string", "enum": ["queued", "already_verified", "recently_sent"] }, "RequestEmailVerificationPayload": { "type": "object", "required": ["language"], "properties": { "language": { "type": "string", "description": "Which language to mail. Falls back to English when no template exists for it." } } }, "ResearchFormQuestionAnswer": { "type": "object", "required": [ "id", "user_id", "course_id", "research_form_question_id", "research_consent", "created_at", "updated_at" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "research_consent": { "type": "boolean" }, "research_form_question_id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "ResendLinkingEmailOutcome": { "type": "string", "enum": [ "queued", "already_mailed_to_every_known_address", "refused_by_rate_cap", "no_address_in_study_registry", "not_on_the_course_roster", "no_student_number_known", "already_linked", "study_registry_unavailable" ] }, "ResendLinkingEmailPayload": { "type": "object", "properties": { "reason": { "type": ["string", "null"] }, "student_number": { "type": ["string", "null"] }, "user_id": { "type": ["string", "null"], "format": "uuid", "description": "One of the two names the person; `user_id` only resolves for an account that has held a number." } } }, "ResendLinkingEmailResult": { "type": "object", "required": ["outcome", "mails_sent_for_this_course", "max_mails_per_person_and_course"], "properties": { "linking_email": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/TeacherLinkingEmailStatus", "description": "The latest mail for this person and course after the attempt, whatever the outcome." } ] }, "mails_sent_for_this_course": { "type": "integer", "format": "int64" }, "max_mails_per_person_and_course": { "type": "integer", "format": "int64" }, "outcome": { "$ref": "#/components/schemas/ResendLinkingEmailOutcome" } } }, "ResetExercisesPayload": { "type": "object", "required": [ "user_ids", "exercise_ids", "reset_all_below_max_points", "reset_only_locked_peer_reviews" ], "properties": { "exercise_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "reset_all_below_max_points": { "type": "boolean" }, "reset_only_locked_peer_reviews": { "type": "boolean" }, "threshold": { "type": ["number", "null"], "format": "double" }, "user_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "ResetPasswordData": { "type": "object", "required": ["token", "new_password"], "properties": { "new_password": { "type": "string" }, "token": { "type": "string" } } }, "ResetPasswordTokenPayload": { "type": "object", "required": ["token"], "properties": { "token": { "type": "string" } } }, "RetryCreditRegistrationOutcome": { "type": "string", "description": "What a retry did to one row. Every refusal is its own value, because the answer a teacher needs\n(\"wait for an admin\" vs \"the student withdrew consent\") differs in each case.", "enum": [ "retried", "refused_submission_uncertain", "refused_consent_withdrawn", "refused_without_consent", "refused_not_failed", "refused_superseded" ] }, "RetryCreditRegistrationPayload": { "type": "object", "properties": { "reason": { "type": ["string", "null"] } } }, "RetryCreditRegistrationResult": { "type": "object", "required": ["outcome", "state"], "properties": { "outcome": { "$ref": "#/components/schemas/RetryCreditRegistrationOutcome" }, "state": { "$ref": "#/components/schemas/CreditRegistrationState", "description": "Where the row stands after the attempt, whatever the outcome." } } }, "RetryCreditRegistrationSkip": { "type": "object", "required": ["outcome", "count"], "properties": { "count": { "type": "integer", "format": "int64" }, "outcome": { "$ref": "#/components/schemas/RetryCreditRegistrationOutcome" } } }, "RetryFailedCreditRegistrationsResult": { "type": "object", "required": [ "retried_count", "skipped", "considered_count", "max_rows_per_call", "more_rows_remaining" ], "properties": { "considered_count": { "type": "integer", "format": "int64", "description": "How many retriable rows this call took, which is what `max_rows_per_call` bounds." }, "max_rows_per_call": { "type": "integer", "format": "int64" }, "more_rows_remaining": { "type": "boolean", "description": "The cap stopped short of the course's retriable failures; running it again takes the next batch." }, "retried_count": { "type": "integer", "format": "int64" }, "skipped": { "type": "array", "items": { "$ref": "#/components/schemas/RetryCreditRegistrationSkip" }, "description": "Why the rest were left alone, so a teacher can see the admin-only pile rather than wonder.\nCourse-wide, not capped: clicking again will not work through these." } } }, "Retryability": { "type": "string", "description": "What may be done about an error code. The class is the contract's, not the endpoint's: the\nimport phase is the only place that upgrades a code to [`Retryability::VerifyOnly`].", "enum": [ "retryable_transient", "verify_only", "permanent_needs_student", "permanent_needs_admin", "permanent_needs_config" ] }, "ReviewingStage": { "type": "string", "description": "\nTells what stage of reviewing the user is currently in. Used for for peer review, self review, and manual review. If an exercise does not involve reviewing, the value of this stage will always be `NotStarted`.", "enum": [ "NotStarted", "PeerReview", "SelfReview", "WaitingForPeerReviews", "WaitingForManualGrading", "ReviewedAndLocked", "Locked", "NotAnsweredAndLocked" ] }, "Role": { "type": "object", "required": ["is_global", "role", "user_id"], "properties": { "course_id": { "type": ["string", "null"], "format": "uuid" }, "course_instance_id": { "type": ["string", "null"], "format": "uuid" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "is_global": { "type": "boolean" }, "organization_id": { "type": ["string", "null"], "format": "uuid" }, "role": { "$ref": "#/components/schemas/UserRole" }, "user_id": { "type": "string", "format": "uuid" } } }, "RoleDomain": { "oneOf": [ { "type": "object", "required": ["tag"], "properties": { "tag": { "type": "string", "enum": ["Global"] } } }, { "type": "object", "required": ["id", "tag"], "properties": { "id": { "type": "string", "format": "uuid" }, "tag": { "type": "string", "enum": ["Organization"] } } }, { "type": "object", "required": ["id", "tag"], "properties": { "id": { "type": "string", "format": "uuid" }, "tag": { "type": "string", "enum": ["Course"] } } }, { "type": "object", "required": ["id", "tag"], "properties": { "id": { "type": "string", "format": "uuid" }, "tag": { "type": "string", "enum": ["CourseInstance"] } } }, { "type": "object", "required": ["id", "tag"], "properties": { "id": { "type": "string", "format": "uuid" }, "tag": { "type": "string", "enum": ["Exam"] } } } ] }, "RoleInfo": { "type": "object", "required": ["email", "role", "domain"], "properties": { "domain": { "$ref": "#/components/schemas/RoleDomain" }, "email": { "type": "string" }, "role": { "$ref": "#/components/schemas/UserRole" } } }, "RoleUser": { "type": "object", "required": ["user_id", "email", "role"], "properties": { "email": { "type": "string" }, "first_name": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "role": { "$ref": "#/components/schemas/UserRole" }, "user_id": { "type": "string", "format": "uuid" } } }, "SaveCourseDesignerScheduleRequest": { "type": "object", "required": ["stages"], "properties": { "name": { "type": ["string", "null"] }, "stages": { "type": "array", "items": { "$ref": "#/components/schemas/CourseDesignerScheduleStageInput" } } } }, "SearchRequest": { "type": "object", "required": ["query"], "properties": { "query": { "type": "string" } } }, "ServiceInfo": { "type": "object", "required": ["name", "ports"], "properties": { "cluster_ip": { "type": ["string", "null"] }, "name": { "type": "string" }, "ports": { "type": "array", "items": { "$ref": "#/components/schemas/ServicePortInfo" } } } }, "ServicePortInfo": { "type": "object", "required": ["port"], "properties": { "name": { "type": ["string", "null"] }, "port": { "type": "integer", "format": "int32" }, "protocol": { "type": ["string", "null"] }, "target_port": { "type": ["string", "null"] } } }, "SetMyCourseCreditRegistrationConsentPayload": { "type": "object", "required": ["consent_given"], "properties": { "consent_given": { "type": "boolean" } } }, "SetMyCourseCreditRegistrationConsentResult": { "type": "object", "required": ["consent_given", "newly_unblocked_registration_count"], "properties": { "consent_given": { "type": "boolean" }, "consent_given_at": { "type": ["string", "null"], "format": "date-time" }, "consent_withdrawn_at": { "type": ["string", "null"], "format": "date-time" }, "newly_unblocked_registration_count": { "type": "integer", "format": "int64" } } }, "SisuDescriptionResponse": { "type": "object", "required": ["course_description", "audience", "modules"], "properties": { "audience": { "type": "array", "items": { "type": "string" } }, "course_description": { "type": "string" }, "modules": { "type": "array", "items": { "$ref": "#/components/schemas/Module" } } } }, "StudentFacingCreditRegistrationStatus": { "type": "string", "description": "The stage a student sees.", "enum": [ "waiting_for_completion", "needs_consent", "needs_student_number", "in_progress", "needs_enrolment", "waiting_for_sisu", "registered", "failed", "not_registering" ] }, "StudentNumberVerificationMethod": { "type": "string", "description": "How a student number was proven to belong to an account.", "enum": ["emailed_link", "email_match_fast_track", "admin_manual"] }, "StudentNumberVerificationTokenPreview": { "type": "object", "description": "What a mailed link would do, without doing it. Read-only on purpose: a mail scanner must not be\nable to spend the token.", "required": [ "student_number", "emailed_to_masked", "expires_at", "expired", "already_used", "already_used_by_this_account", "conflicts_with_other_account", "target_account_email", "claimable" ], "properties": { "already_used": { "type": "boolean" }, "already_used_by_this_account": { "type": "boolean", "description": "So the page can say \"you already used this link\" rather than accusing someone else." }, "claimable": { "type": "boolean" }, "conflicts_with_other_account": { "type": "boolean", "description": "A support case, not something the student can resolve: moving a number between accounts on\nmailbox access alone would let anyone detach another account's link." }, "course_name": { "type": ["string", "null"] }, "current_student_number": { "type": ["string", "null"], "description": "What this account is linked to now. Claiming replaces it." }, "emailed_to_masked": { "type": "string" }, "expired": { "type": "boolean" }, "expires_at": { "type": "string", "format": "date-time" }, "first_names": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "student_number": { "type": "string" }, "target_account_email": { "type": "string", "description": "Shown in the confirmation: being signed in to the wrong account is the common mistake." } } }, "StudentsByCountryTotalsResult": { "type": "object", "required": ["count"], "properties": { "count": { "type": "integer", "format": "int64" }, "country": { "type": ["string", "null"] } } }, "StudentsListPage": { "type": "object", "description": "A page of the student identity list plus the total number of pages for the current filters.", "required": ["data", "total_pages"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/CourseStudentListRow" } }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 } } }, "SuotarApiCallDetails": { "type": "object", "required": ["call", "ledger_references", "events"], "properties": { "call": { "$ref": "#/components/schemas/SuotarApiCallRow" }, "error_message": { "type": ["string", "null"], "description": "Our own wording; the registry's error prose is stored for nobody." }, "events": { "type": "array", "items": { "$ref": "#/components/schemas/SuotarApiCallEvent" } }, "ledger_references": { "type": "array", "items": { "$ref": "#/components/schemas/SuotarApiCallLedgerReference" } }, "request_body_sample": { "description": "Scrubbed and truncated when it was written; never un-scrubbed for display." }, "response_body_sample": {} } }, "SuotarApiCallEvent": { "type": "object", "description": "A timeline entry written against this call, one per item the answer moved.", "required": ["id", "credit_registration_id", "created_at", "kind"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "credit_registration_id": { "type": "string", "format": "uuid" }, "details": { "description": "The `{request, response}` pair for this one item, scrubbed at write time." }, "error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "from_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] }, "id": { "type": "string", "format": "uuid" }, "kind": { "$ref": "#/components/schemas/CreditRegistrationEventKind" }, "to_state": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationState" } ] } } }, "SuotarApiCallLedgerReference": { "type": "object", "description": "One ledger row a call carried, resolved from `credit_registration_ids`. This is what stands in\nfor the redacted body: the identifiers are here, beside the item they belong to.", "required": [ "credit_registration_id", "request_item_id", "user_id", "course_id", "course_name", "state" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "credit_registration_id": { "type": "string", "format": "uuid" }, "email": { "type": ["string", "null"] }, "error_code": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/CreditRegistrationErrorCode" } ] }, "first_name": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "request_item_id": { "type": "string", "description": "The id the registry saw for this row, so a line of the stored body maps to a student." }, "state": { "$ref": "#/components/schemas/CreditRegistrationState" }, "student_number": { "type": ["string", "null"] }, "user_id": { "type": "string", "format": "uuid" } } }, "SuotarApiCallRow": { "type": "object", "required": [ "id", "endpoint", "started_at", "succeeded", "request_item_count", "ok_item_count", "error_item_count", "worker_name", "credit_registration_ids" ], "properties": { "credit_registration_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "duration_ms": { "type": ["integer", "null"], "format": "int32" }, "endpoint": { "$ref": "#/components/schemas/SuotarEndpoint" }, "error_item_count": { "type": "integer", "format": "int32" }, "http_status": { "type": ["integer", "null"], "format": "int32", "description": "`None` with `succeeded = false` means the request never got an answer: connect, TLS or\ntimeout." }, "id": { "type": "string", "format": "uuid" }, "ok_item_count": { "type": "integer", "format": "int32" }, "request_item_count": { "type": "integer", "format": "int32" }, "request_level_error_code": { "type": ["string", "null"], "description": "The registry's own request-level code, an identifier rather than prose." }, "started_at": { "type": "string", "format": "date-time" }, "succeeded": { "type": "boolean" }, "worker_name": { "type": "string" } } }, "SuotarApiCallsPage": { "type": "object", "required": ["data", "total_count", "total_pages", "worker_names"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/SuotarApiCallRow" } }, "total_count": { "type": "integer", "format": "int64" }, "total_pages": { "type": "integer", "format": "int32", "minimum": 0 }, "worker_names": { "type": "array", "items": { "type": "string" }, "description": "The `worker_name` values in the log, for the filter." } } }, "SuotarEndpoint": { "type": "string", "enum": [ "resolve_persons", "resolve_enrolments", "import_attainments", "verify_attainments", "product_access_tokens", "list_by_course" ] }, "SuotarEndpointStanding": { "type": "object", "description": "Where one study registry endpoint stands, over all time.", "required": ["endpoint", "consecutive_failures"], "properties": { "consecutive_failures": { "type": "integer", "format": "int64" }, "endpoint": { "$ref": "#/components/schemas/SuotarEndpoint" }, "last_failure_at": { "type": ["string", "null"], "format": "date-time" }, "last_success_at": { "type": ["string", "null"], "format": "date-time" } } }, "SuotarEndpointWindowStats": { "type": "object", "required": [ "endpoint", "call_count", "failed_call_count", "in_flight_count", "ok_item_count", "error_item_count" ], "properties": { "call_count": { "type": "integer", "format": "int64" }, "endpoint": { "$ref": "#/components/schemas/SuotarEndpoint" }, "error_item_count": { "type": "integer", "format": "int64" }, "failed_call_count": { "type": "integer", "format": "int64" }, "in_flight_count": { "type": "integer", "format": "int64" }, "last_failure_at": { "type": ["string", "null"], "format": "date-time" }, "last_request_level_error_code": { "type": ["string", "null"], "description": "The registry's own request-level code, an identifier rather than prose." }, "last_success_at": { "type": ["string", "null"], "format": "date-time" }, "ok_item_count": { "type": "integer", "format": "int64" }, "p50_duration_ms": { "type": ["integer", "null"], "format": "int32" }, "p95_duration_ms": { "type": ["integer", "null"], "format": "int32" } } }, "SuotarHealth": { "type": "object", "required": ["windows"], "properties": { "windows": { "type": "array", "items": { "$ref": "#/components/schemas/SuotarHealthWindow" } } } }, "SuotarHealthWindow": { "type": "object", "required": ["window_secs", "endpoints"], "properties": { "endpoints": { "type": "array", "items": { "$ref": "#/components/schemas/SuotarEndpointWindowStats" } }, "window_secs": { "type": "integer", "format": "int64" } } }, "SuspectedCheaterStatus": { "type": "string", "description": "Review state of a suspected cheater.", "enum": ["Flagged", "ConfirmedCheating", "Dismissed"] }, "SuspectedCheaters": { "type": "object", "required": ["id", "user_id", "course_id", "created_at", "total_points", "status"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_module_id": { "type": ["string", "null"], "format": "uuid", "description": "The module completion that triggered the flag. `None` only for legacy rows the backfill\ncouldn't map to a default module." }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "status": { "$ref": "#/components/schemas/SuspectedCheaterStatus" }, "total_duration_seconds": { "type": ["integer", "null"], "format": "int32" }, "total_points": { "type": "integer", "format": "int32" }, "updated_at": { "type": ["string", "null"], "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "SystemHealthStatus": { "type": "object", "required": ["status", "issues"], "properties": { "issues": { "type": "array", "items": { "type": "string" } }, "status": { "$ref": "#/components/schemas/HealthStatus" } } }, "TeacherDecisionType": { "type": "string", "enum": [ "FullPoints", "ZeroPoints", "CustomPoints", "SuspectedPlagiarism", "RejectAndReset", "UnauthorizedAiUse", "BadAnswer", "Other" ] }, "TeacherGradingDecision": { "type": "object", "required": [ "id", "user_exercise_state_id", "created_at", "updated_at", "score_given", "teacher_decision" ], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "hidden": { "type": ["boolean", "null"] }, "id": { "type": "string", "format": "uuid" }, "justification": { "type": ["string", "null"] }, "score_given": { "type": "number", "format": "float" }, "teacher_decision": { "$ref": "#/components/schemas/TeacherDecisionType" }, "updated_at": { "type": "string", "format": "date-time" }, "user_exercise_state_id": { "type": "string", "format": "uuid" }, "user_id": { "type": ["string", "null"], "format": "uuid" } } }, "TeacherLinkingEmailStatus": { "type": "object", "description": "What we can honestly say about a linking mail: our send status and the address's domain.", "required": ["email_send_status", "retry_count", "emailed_to_masked"], "properties": { "email_send_status": { "$ref": "#/components/schemas/EmailSendStatus" }, "emailed_to_masked": { "type": "string" }, "last_attempt_at": { "type": ["string", "null"], "format": "date-time" }, "next_retry_at": { "type": ["string", "null"], "format": "date-time" }, "retry_count": { "type": "integer", "format": "int32" }, "sent_at": { "type": ["string", "null"], "format": "date-time" } } }, "TeacherManualCompletion": { "type": "object", "required": ["user_id", "passed"], "properties": { "completion_date": { "type": ["string", "null"], "format": "date-time" }, "grade": { "type": ["integer", "null"], "format": "int32" }, "passed": { "type": "boolean" }, "user_id": { "type": "string", "format": "uuid" } } }, "TeacherManualCompletionRequest": { "type": "object", "required": ["course_module_id", "new_completions", "skip_duplicate_completions"], "properties": { "course_module_id": { "type": "string", "format": "uuid" }, "new_completions": { "type": "array", "items": { "$ref": "#/components/schemas/TeacherManualCompletion" } }, "skip_duplicate_completions": { "type": "boolean" } } }, "Term": { "type": "object", "required": ["id", "created_at", "updated_at", "term", "definition", "course_id"], "properties": { "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "definition": { "type": "string" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "term": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" } } }, "TermUpdate": { "type": "object", "required": ["term", "definition"], "properties": { "definition": { "type": "string" }, "term": { "type": "string" } } }, "ThresholdData": { "type": "object", "required": ["duration_seconds"], "properties": { "duration_seconds": { "type": "integer", "format": "int32" } } }, "TimeGranularity": { "type": "string", "enum": ["Year", "Month", "Day"] }, "ToolCategory": { "type": "string", "description": "A category of chatbot tools a configuration can choose to offer the LLM. Independent of the\nchatbot crate's per-tool `ToolPermission` check: a category answers \"does this chatbot offer\nthis kind of tool\", not \"may this caller use it\".", "enum": [ "course_material", "course_info", "course_catalog", "interaction", "admin_support_accounts", "admin_support_courses", "admin_support_learning_progress", "admin_support_academic_integrity" ] }, "UnlinkMyStudentNumberResult": { "type": "object", "required": ["affected_registration_count"], "properties": { "affected_registration_count": { "type": "integer", "format": "int64", "description": "Registrations that went back to waiting for a student number." } } }, "UpdateCourseDesignerStageTaskRequest": { "type": "object", "properties": { "description": { "type": ["string", "null"] }, "is_completed": { "type": ["boolean", "null"] }, "title": { "type": ["string", "null"] } } }, "UploadResult": { "type": "object", "description": "Result of a image upload. Tells where the uploaded image can be retrieved from.", "required": ["url"], "properties": { "url": { "type": "string" } } }, "User": { "type": "object", "required": ["id", "created_at", "updated_at"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "email_domain": { "type": ["string", "null"] }, "id": { "type": "string", "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "upstream_id": { "type": ["integer", "null"], "format": "int32" } } }, "UserCertificate": { "type": "object", "description": "A certificate as its holder's own profile lists it: what it is for, and how to open it.", "required": [ "id", "name_on_certificate", "verification_id", "created_at", "course_id", "course_name" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": ["string", "null"], "description": "`None` for a course's default module." }, "course_name": { "type": "string" }, "created_at": { "type": "string", "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "name_on_certificate": { "type": "string" }, "verification_id": { "type": "string", "description": "Addresses the public validation page, which is also how the holder views the image." } } }, "UserChapterLockingStatus": { "type": "object", "required": [ "id", "created_at", "updated_at", "user_id", "chapter_id", "course_id", "status" ], "properties": { "chapter_id": { "type": "string", "format": "uuid" }, "course_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "status": { "$ref": "#/components/schemas/ChapterLockingStatus" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "UserChapterProgress": { "type": "object", "required": [ "user_id", "chapter_id", "chapter_number", "chapter_name", "points_obtained", "exercises_attempted" ], "properties": { "chapter_id": { "type": "string", "format": "uuid" }, "chapter_name": { "type": "string" }, "chapter_number": { "type": "integer", "format": "int32" }, "exercises_attempted": { "type": "integer", "format": "int64" }, "points_obtained": { "type": "number", "format": "double" }, "user_id": { "type": "string", "format": "uuid" } } }, "UserCompletionInformation": { "type": "object", "required": [ "course_module_completion_id", "course_name", "email", "enable_registering_completion_to_uh_open_university", "enable_credit_registration_via_suotar" ], "properties": { "course_module_completion_id": { "type": "string", "format": "uuid" }, "course_name": { "type": "string" }, "ects_credits": { "type": ["number", "null"], "format": "float" }, "email": { "type": "string" }, "enable_credit_registration_via_suotar": { "type": "boolean" }, "enable_registering_completion_to_uh_open_university": { "type": "boolean" }, "uh_course_code": { "type": ["string", "null"], "description": "`None` only on a module registering through credit registration." } } }, "UserCourseProgress": { "type": "object", "required": [ "course_module_id", "course_module_name", "course_module_order_number", "score_given" ], "properties": { "attempted_exercises": { "type": ["integer", "null"], "format": "int32" }, "attempted_exercises_required": { "type": ["integer", "null"], "format": "int32" }, "course_module_id": { "type": "string", "format": "uuid" }, "course_module_name": { "type": "string" }, "course_module_order_number": { "type": "integer", "format": "int32" }, "score_given": { "type": "number", "format": "float" }, "score_maximum": { "type": ["integer", "null"], "format": "int32", "minimum": 0 }, "score_required": { "type": ["integer", "null"], "format": "int32" }, "total_exercises": { "type": ["integer", "null"], "format": "int32", "minimum": 0 } } }, "UserCourseSettings": { "type": "object", "required": [ "user_id", "course_language_group_id", "created_at", "updated_at", "current_course_id", "current_course_instance_id", "hidden" ], "properties": { "course_language_group_id": { "type": "string", "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "current_course_id": { "type": "string", "format": "uuid" }, "current_course_instance_id": { "type": "string", "format": "uuid" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "hidden": { "type": "boolean", "description": "Whether the user has hidden this course from their personal \"My courses\" list. Does not\naffect course progress." }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "UserCourseSubmissionTime": { "type": "object", "description": "A single submission's time, its exercise, and the module that exercise sits in (via its chapter).\nUsed to plot a user's submission activity within a course.", "required": ["created_at", "exercise_id"], "properties": { "course_module_id": { "type": ["string", "null"], "format": "uuid", "description": "Module the exercise's chapter belongs to; `None` for exercises not placed in a chapter." }, "created_at": { "type": "string", "format": "date-time" }, "exercise_id": { "type": "string", "format": "uuid" } } }, "UserDetail": { "type": "object", "required": ["user_id", "created_at", "updated_at", "email"], "properties": { "country": { "type": ["string", "null"] }, "created_at": { "type": "string", "format": "date-time" }, "email": { "type": "string" }, "email_communication_consent": { "type": ["boolean", "null"] }, "email_verified_at": { "type": ["string", "null"], "format": "date-time", "description": "When the user last proved control of the address in `email`. `None` means unproven. Cleared\nby a database trigger on every address change, so a value here always refers to `email`." }, "email_verified_method": { "oneOf": [ { "type": "null" }, { "$ref": "#/components/schemas/EmailVerificationMethod" } ] }, "first_name": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "search_helper": { "type": ["string", "null"] }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "UserDetailsRequest": { "type": "object", "required": ["user_id", "course_ids"], "properties": { "course_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "user_id": { "type": "string", "format": "uuid" } } }, "UserExerciseState": { "type": "object", "required": [ "id", "user_id", "exercise_id", "created_at", "updated_at", "grading_progress", "activity_progress", "reviewing_stage" ], "properties": { "activity_progress": { "$ref": "#/components/schemas/ActivityProgress" }, "course_id": { "type": ["string", "null"], "format": "uuid" }, "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "exam_id": { "type": ["string", "null"], "format": "uuid" }, "exercise_id": { "type": "string", "format": "uuid" }, "grading_progress": { "$ref": "#/components/schemas/GradingProgress" }, "id": { "type": "string", "format": "uuid" }, "reviewing_stage": { "$ref": "#/components/schemas/ReviewingStage" }, "score_given": { "type": ["number", "null"], "format": "float" }, "selected_exercise_slide_id": { "type": ["string", "null"], "format": "uuid" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "UserIdsPayload": { "type": "object", "description": "Body for the batch detail endpoints: the users of the current identity-list page.", "required": ["user_ids"], "properties": { "user_ids": { "type": "array", "items": { "type": "string", "format": "uuid" } } } }, "UserInfoPayload": { "type": "object", "required": ["email", "first_name", "last_name", "country", "email_communication_consent"], "properties": { "country": { "type": "string" }, "email": { "type": "string" }, "email_communication_consent": { "type": "boolean" }, "first_name": { "type": "string" }, "last_name": { "type": "string" } } }, "UserPointsUpdateStrategy": { "type": "string", "enum": ["CanAddPointsButCannotRemovePoints", "CanAddPointsAndCanRemovePoints"] }, "UserResearchConsent": { "type": "object", "required": ["id", "user_id", "research_consent", "created_at", "updated_at"], "properties": { "created_at": { "type": "string", "format": "date-time" }, "deleted_at": { "type": ["string", "null"], "format": "date-time" }, "id": { "type": "string", "format": "uuid" }, "research_consent": { "type": "boolean" }, "updated_at": { "type": "string", "format": "date-time" }, "user_id": { "type": "string", "format": "uuid" } } }, "UserRole": { "type": "string", "enum": [ "Reviewer", "Assistant", "Teacher", "Admin", "CourseOrExamCreator", "MaterialViewer", "TeachingAndLearningServices", "StatsViewer" ] }, "UserSuspectedCheaterInfo": { "type": "object", "description": "A user's suspected-cheater record in one course, paired with that course's duration threshold.\nRead-only, for the cross-course \"Completion review\" list on the user-details page.", "required": [ "course_id", "status", "total_points", "first_flagged_at", "threshold_seconds" ], "properties": { "course_id": { "type": "string", "format": "uuid" }, "first_flagged_at": { "type": "string", "format": "date-time", "description": "When first flagged in this course (record `created_at`, unchanged on re-flag)." }, "status": { "$ref": "#/components/schemas/SuspectedCheaterStatus" }, "threshold_seconds": { "type": "integer", "format": "int32", "description": "Threshold (seconds) of the module that triggered the flag; the student completed faster." }, "total_duration_seconds": { "type": ["integer", "null"], "format": "int32" }, "total_points": { "type": "integer", "format": "int32" } } }, "UserWithModuleCompletions": { "type": "object", "required": ["completed_modules", "email", "user_id"], "properties": { "completed_modules": { "type": "array", "items": { "$ref": "#/components/schemas/CourseModuleCompletionWithRegistrationInfo" } }, "email": { "type": "string" }, "first_name": { "type": ["string", "null"] }, "last_name": { "type": ["string", "null"] }, "user_id": { "type": "string", "format": "uuid" } } }, "VerbosityLevel": { "type": "string", "enum": ["low", "medium", "high"] }, "VerifiedStudentNumberMethodTotal": { "type": "object", "required": ["verified_via", "count"], "properties": { "count": { "type": "integer", "format": "int64" }, "verified_via": { "$ref": "#/components/schemas/StudentNumberVerificationMethod" } } }, "VerifyEmailOwnershipPayload": { "type": "object", "required": ["code"], "properties": { "code": { "type": "string" } } }, "VerifyEmailOwnershipResult": { "type": "string", "description": "Outcome of submitting a code. Wrong, expired, superseded and spent are one value: they are\nindistinguishable to someone typing digits, and telling them apart only helps a guesser.", "enum": ["verified", "already_verified", "invalid"] } } }, "tags": [ { "name": "glossary", "description": "Glossary endpoints used by main-frontend" } ] }