Задания
CRUD заданий, адресная выдача заданий группам, прикреплённые преподавателем файлы и жизненный цикл работ студента: создание/получение черновика, редактирование, отправка (с несколькими файлами), история попыток, проверка и переоткрытие. Все эндпоинты живут под /api/v1/t/* и требуют JWT + заголовок X-Tenant-Subdomain.
- GET
/api/v1/t/assignmentsСписок заданий - GET
/api/v1/t/assignments/progressПрогресс по заданиям (студент) - GET
/api/v1/t/assignments/:idПолучить задание - POST
/api/v1/t/assignmentsСоздать задание - PATCH
/api/v1/t/assignments/:idИзменить задание - DELETE
/api/v1/t/assignments/:idУдалить задание - POST
/api/v1/t/assignments/:id/attachmentsПрикрепить файл к заданию - DELETE
/api/v1/t/assignments/:id/attachments/:attachmentIdОткрепить файл от задания - GET
/api/v1/t/assignments/:id/attachments/:attachmentId/downloadСкачать файл задания - GET
/api/v1/t/assignments/:id/group-targetsЦелевые группы задания - PUT
/api/v1/t/assignments/:id/group-targetsЗадать целевые группы задания - POST
/api/v1/t/assignments/:id/submissionsСоздать или получить черновик работы - GET
/api/v1/t/assignments/:id/submissionsСписок работ по заданию - GET
/api/v1/t/assignments/:id/submissions/historyИстория попыток по заданию - GET
/api/v1/t/submissions/:idПолучить работу - PATCH
/api/v1/t/submissions/:idИзменить черновик работы - POST
/api/v1/t/submissions/:id/submitОтправить работу - POST
/api/v1/t/submissions/:id/reviewПроверить работу (Принят / Не принят) - POST
/api/v1/t/submissions/:id/reopenПереоткрыть работу
/api/v1/t/assignmentsСписок заданий
Постраничный список заданий школы. Доступен любому авторизованному пользователю школы. Списочные строки не гидрируют вложения (attachments остаётся пустым). Для роли student список фильтруется по доступу через группы, а каждая строка дополняется полями my_submission_status и my_submission_score — статусом (draft/submitted/accepted/rejected) и оценкой работы текущего студента.
Конверт ответа: { assignments, total, page, limit }. page с 1; по умолчанию page=1, limit=20 (1..100). Поля my_submission_status (draft/submitted/accepted/rejected) / my_submission_score присутствуют только в ответе студенту (для остальных ролей опускаются).
Query-параметры
| Параметр | Тип | Описание |
|---|---|---|
qопц. | string | Поиск по названию задания. |
pageопц. | integer | Номер страницы (с 1). По умолчанию 1.Пример: 1 |
limitопц. | integer | Размер страницы (1..100). По умолчанию 20.Пример: 20 |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/assignments?page=1&limit=20" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"assignments": [
{
"id": "3c3c...",
"title": "Эссе по главе 1",
"instructions": "<p>Напишите эссе...</p>",
"max_score": "100",
"attachments": [],
"my_submission_status": "submitted",
"my_submission_score": null,
"created_at": "2026-05-28T09:00:00Z",
"updated_at": "2026-05-28T09:00:00Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_params | Некорректные page/limit (page<1, limit<1 или limit>100). |
| 401 | unauthorized | Школа не определена или токен недействителен. |
/api/v1/t/assignments/progressПрогресс по заданиям (студент)
Возвращает агрегат прогресса текущего студента по заданиям: accessible — доступные студенту задания (без таргетинга на группы либо студент в целевой группе), completed — из них с хотя бы одной отправленной работой (статус не draft: submitted/accepted/rejected), percent — процент выполнения. Прогресс считается по факту отправки, а не по решению проверки. Только для роли student.
Пример запроса
curl https://api.edumentor.kz/api/v1/t/assignments/progress \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": { "completed": 3, "accessible": 5, "percent": 60 }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | student_only | Эндпоинт доступен только роли student. |
/api/v1/t/assignments/:idПолучить задание
Возвращает задание с гидрированным списком прикреплённых преподавателем файлов (имя, размер, тип, mime). Кросс-тенантный ID отдаётся как 404.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/assignments/3c3c... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "3c3c...",
"title": "Эссе по главе 1",
"instructions": "<p>Напишите эссе...</p>",
"max_score": "100",
"attachments": [
{
"id": "a1a1...",
"media_asset_id": "m1m1...",
"position": 0,
"filename": "rubric.pdf",
"size": 20480,
"kind": "document",
"mime": "application/pdf"
}
],
"created_at": "2026-05-28T09:00:00Z",
"updated_at": "2026-05-28T09:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
/api/v1/t/assignmentsСоздать задание
Создаёт новое задание. instructions — необработанный HTML, очищается на сервере. max_score — числовая строка (например, «100» или «50.5») для сохранения точности NUMERIC.
201 Created.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
titleобяз. | string | Название задания (1..255). |
instructionsопц. | string | HTML-инструкции (очищаются сервером). |
max_scoreопц. | string | Максимальный балл как числовая строка.Пример: 100 |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/assignments \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "title": "Эссе по главе 1", "instructions": "<p>Напишите эссе...</p>", "max_score": "100" }'Пример ответа
{
"success": true,
"data": {
"id": "3c3c...",
"title": "Эссе по главе 1",
"instructions": "<p>Напишите эссе...</p>",
"max_score": "100",
"attachments": [],
"created_at": "2026-05-28T09:00:00Z",
"updated_at": "2026-05-28T09:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_request_body | Некорректное тело запроса (нарушены binding-правила). |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_courses. |
/api/v1/t/assignments/:idИзменить задание
Частичное обновление задания. Переданные поля заменяют значения; отсутствующие остаются прежними.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
titleопц. | string | Новое название (1..255). |
instructionsопц. | string | Новые HTML-инструкции. |
max_scoreопц. | string | Новый максимальный балл как числовая строка. |
Пример запроса
curl -X PATCH https://api.edumentor.kz/api/v1/t/assignments/3c3c... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "max_score": "80" }'Пример ответа
{
"success": true,
"data": {
"id": "3c3c...",
"title": "Эссе по главе 1",
"instructions": "<p>Напишите эссе...</p>",
"max_score": "80",
"attachments": [],
"created_at": "2026-05-28T09:00:00Z",
"updated_at": "2026-05-28T12:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 400 | invalid_request_body | Некорректное тело или невалидный max_score. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_courses. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
/api/v1/t/assignments/:idУдалить задание
Мягко удаляет задание. 204 No Content при успехе.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Пример запроса
curl -X DELETE https://api.edumentor.kz/api/v1/t/assignments/3c3c... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
HTTP/1.1 204 No ContentОшибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_courses. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
/api/v1/t/assignments/:id/attachmentsПрикрепить файл к заданию
Прикрепляет существующий медиа-актив к заданию (файл преподавателя — отдельно от вложений работ студентов). position необязателен — при отсутствии берётся max(position)+1.
201 Created с гидрированным AssignmentAttachmentDTO.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
media_asset_idобяз. | uuid | ID медиа-актива из библиотеки. |
positionопц. | integer | Позиция в списке (>=0). По умолчанию — в конец. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/assignments/3c3c.../attachments \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "media_asset_id": "m1m1...", "position": 0 }'Пример ответа
{
"success": true,
"data": {
"id": "a1a1...",
"media_asset_id": "m1m1...",
"position": 0,
"filename": "rubric.pdf",
"size": 20480,
"kind": "document",
"mime": "application/pdf"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 400 | invalid_request_body | Некорректное тело запроса. |
| 400 | attachment_not_in_tenant | Медиа-актив принадлежит другой школе. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_courses. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
| 409 | attachment_duplicate | Этот медиа-актив уже прикреплён к заданию. |
/api/v1/t/assignments/:id/attachments/:attachmentIdОткрепить файл от задания
Удаляет прикреплённый к заданию файл. 204 No Content при успехе.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
attachmentIdобяз. | uuid | ID вложения задания. |
Пример запроса
curl -X DELETE https://api.edumentor.kz/api/v1/t/assignments/3c3c.../attachments/a1a1... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
HTTP/1.1 204 No ContentОшибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID задания или вложения не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_courses. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
| 404 | attachment_not_found | Вложение не найдено. |
/api/v1/t/assignments/:id/attachments/:attachmentId/downloadСкачать файл задания
Возвращает временную (presigned) ссылку на скачивание прикреплённого к заданию файла. Доступ — менеджер курсов (admin/superadmin или куратор с manage_courses) ИЛИ студент, записанный на курс с этим заданием.
Проверка прав выполняется внутри обработчика (а не на уровне middleware), чтобы записанные студенты тоже проходили. Самостоятельные задания (не привязанные к блокам курса) скачивают только менеджеры курсов.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
attachmentIdобяз. | uuid | ID вложения задания. |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/assignments/3c3c.../attachments/a1a1.../download" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": "https://s3.edumentor.kz/media/...&X-Amz-Signature=..."
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID задания или вложения не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | not_authorized | Вызывающий не менеджер курсов и не записан на курс с этим заданием. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
| 404 | attachment_not_found | Вложение не найдено. |
/api/v1/t/assignments/:id/group-targetsЦелевые группы задания
Возвращает список UUID групп, которым адресовано задание (allow-list). Пустой массив означает, что задание доступно всем зачисленным студентам.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/assignments/3c3c.../group-targets \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": { "group_ids": ["g1a2b3c4-...", "g2b3c4d5-..."] }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_courses. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
/api/v1/t/assignments/:id/group-targetsЗадать целевые группы задания
Атомарно перезаписывает allow-list групп задания. Если задана хотя бы одна группа — сдавать задание могут только студенты из этих групп (иначе сервер вернёт 403 при создании работы); пустой или опущенный массив открывает задание всем зачисленным.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
group_idsопц. | string[] | Список UUID целевых групп (пустой/опущенный — открыть всем). |
Пример запроса
curl -X PUT https://api.edumentor.kz/api/v1/t/assignments/3c3c.../group-targets \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "group_ids": ["g1a2b3c4-..."] }'Пример ответа
{
"success": true,
"data": { "group_ids": ["g1a2b3c4-..."] }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 400 | invalid_request_body | Тело запроса не прошло валидацию. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_courses. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
/api/v1/t/assignments/:id/submissionsСоздать или получить черновик работы
Студент создаёт (или получает существующий) черновик работы по заданию. student_id берётся из JWT, не из тела. Если задание адресовано конкретным группам, а студент не входит ни в одну из них — 403. Возвращает 201 с полным SubmissionResponse.
Можно сразу приложить файлы: при теле multipart/form-data сервер загружает файлы (поле attachments) и привязывает их к черновику. Повторные попытки (first-class attempts): если текущая работа отклонена (status=rejected), вызов создаёт НОВУЮ попытку-черновик с новым id и attempt += 1 — отклонённая попытка сохраняется как история (полная история на .../submissions/history). Статусы draft/submitted/accepted возвращаются как есть (тот же id), без создания новой попытки. JSON-тело с attachment_ids также принимается (обратная совместимость).
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
attachmentsопц. | file[] | Файлы (multipart/form-data) — загружаются и сразу привязываются к черновику. Действует лимит SubmissionMaxFiles. |
attachment_idsопц. | string[] | Альтернатива multipart: список UUID предзагруженных медиа-активов (для JSON-тела). |
Пример запроса
# Пустой черновик
curl -X POST https://api.edumentor.kz/api/v1/t/assignments/3c3c.../submissions \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"
# Сразу с файлами (multipart, можно несколько — до 5)
curl -X POST https://api.edumentor.kz/api/v1/t/assignments/3c3c.../submissions \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-F "attachments=@/path/to/essay.pdf" \
-F "attachments=@/path/to/appendix.pdf"Пример ответа
{
"success": true,
"data": {
"id": "5b5b...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"status": "draft",
"score": null,
"feedback": null,
"graded_by_id": null,
"graded_at": null,
"submitted_at": null,
"attachments": [],
"created_at": "2026-05-28T10:00:00Z",
"updated_at": "2026-05-28T10:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID задания в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | student_not_in_targeted_group | Задание адресовано конкретным группам, а студент не входит ни в одну из них. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
/api/v1/t/assignments/:id/submissionsСписок работ по заданию
Ролевой список работ по заданию. Менеджер (admin/superadmin или куратор с manage_students) получает полный рабочий лист: непривилегированный куратор видит только работы своих студентов, admin/superadmin/привилегированный куратор — все работы. Студент (и любой без manage_students) получает только собственную работу по этому заданию — в той же обёртке { submissions, total } (пустой список, если своей работы ещё нет).
Проверка прав ролевая и выполняется внутри обработчика (а не жёстким manage_students-гейтом на маршруте): наличие manage_students определяет режим (полный рабочий лист vs. только своя работа). Для строк рабочего листа менеджеру SubmissionResponse дополнительно содержит student_email / student_first_name / student_last_name; в режиме студента эти поля опускаются. Каждое вложение (SubmissionAttachmentDTO) теперь гидрируется временной ссылкой: filename, presigned_url, expires_in (секунды) — отдельный запрос к /media не нужен.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Query-параметры
| Параметр | Тип | Описание |
|---|---|---|
statusопц. | string | Фильтр по статусу работы: draft | submitted | accepted | rejected. |
limitопц. | integer | Размер страницы. По умолчанию 50.Пример: 50 |
offsetопц. | integer | Смещение. По умолчанию 0.Пример: 0 |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/assignments/3c3c.../submissions?status=accepted&limit=50&offset=0" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"submissions": [
{
"id": "5b5b...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"student_email": "student@acme.kz",
"student_first_name": "Айгерим",
"student_last_name": "Нурлан",
"status": "submitted",
"score": null,
"feedback": null,
"graded_by_id": null,
"graded_at": null,
"submitted_at": "2026-05-28T11:00:00Z",
"attachments": [
{
"id": "att1...",
"media_asset_id": "m2m2...",
"position": 0,
"filename": "essay.pdf",
"presigned_url": "https://s3.edumentor.kz/media/...&X-Amz-Signature=...",
"expires_in": 900
}
],
"created_at": "2026-05-28T10:00:00Z",
"updated_at": "2026-05-28T11:00:00Z"
}
],
"total": 1
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID задания в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
/api/v1/t/assignments/:id/submissions/historyИстория попыток по заданию
Возвращает ПОЛНУЮ историю попыток по заданию (first-class attempts): каждая попытка — отдельная строка со своим номером attempt. Это read-путь для «показать прошлые попытки и их фидбэк». Ролевой: студент видит только свои попытки; менеджер (manage_students) — историю конкретного студента через ?student_id, либо, без параметра, попытки всех студентов. Непривилегированный куратор ограничен своим набором my_students. Каждая работа дополнена полем attempt (1-based номер попытки студента, 1 = самая ранняя); список отсортирован новейшие-сначала.
Конверт ответа: { attempts, total }. Каждая попытка имеет полный набор полей (status/score/feedback/attachments с presigned-ссылками) — её файлы сохраняются. Балл и фидбэк отклонённой работы доступны здесь даже после того, как студент начал новую попытку. Проверка прав ролевая и выполняется внутри обработчика (нет жёсткого manage_students-гейта на маршруте), чтобы студент мог увидеть собственную историю.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID задания. |
Query-параметры
| Параметр | Тип | Описание |
|---|---|---|
student_idопц. | uuid | Только для менеджера (manage_students): ограничить историю одним студентом. Для студента игнорируется — всегда возвращается собственная история. |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/assignments/3c3c.../submissions/history" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"attempts": [
{
"id": "5b5b...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"status": "draft",
"attempt": 2,
"score": null,
"feedback": null,
"graded_by_id": null,
"graded_at": null,
"submitted_at": null,
"attachments": [],
"created_at": "2026-06-24T10:00:00Z",
"updated_at": "2026-06-24T10:00:00Z"
},
{
"id": "4a4a...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"status": "rejected",
"attempt": 1,
"score": "40",
"feedback": "<p>Доработайте раздел 2</p>",
"graded_by_id": "c1c1...",
"graded_at": "2026-06-22T12:00:00Z",
"submitted_at": "2026-06-21T09:00:00Z",
"attachments": [
{
"id": "att0...",
"media_asset_id": "m1m1...",
"position": 0,
"filename": "v1.pdf",
"presigned_url": "https://s3.edumentor.kz/media/...&X-Amz-Signature=...",
"expires_in": 900
}
],
"created_at": "2026-06-20T09:00:00Z",
"updated_at": "2026-06-22T12:00:00Z"
}
],
"total": 2
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID задания или student_id не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 404 | assignment_not_found | Задание отсутствует или принадлежит другой школе. |
/api/v1/t/submissions/:idПолучить работу
Возвращает одну работу. Доступ: владелец (студент) ИЛИ менеджер студентов (manage_students). Для непривилегированного куратора работа вне его охвата схлопывается в 404.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID работы. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/submissions/5b5b... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "5b5b...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"status": "submitted",
"score": null,
"feedback": null,
"graded_by_id": null,
"graded_at": null,
"submitted_at": "2026-05-28T11:00:00Z",
"attachments": [
{
"id": "att1...",
"media_asset_id": "m2m2...",
"position": 0,
"filename": "essay.pdf",
"presigned_url": "https://s3.edumentor.kz/media/...&X-Amz-Signature=...",
"expires_in": 900
}
],
"created_at": "2026-05-28T10:00:00Z",
"updated_at": "2026-05-28T11:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 404 | submission_not_found | Работа отсутствует, другая школа или вне охвата (схлопывание для приватности). |
/api/v1/t/submissions/:idИзменить черновик работы
Студент обновляет список вложений своего черновика. Доступ только владельца; статус должен быть draft. Перезапись полностью заменяет список вложений.
Принимается ЛИБО JSON-тело с attachment_ids (предзагруженные media UUID), ЛИБО multipart/form-data с самими файлами (любое имя поля для файловых частей) — в этом случае сервер загружает файлы и использует их как итоговый набор вложений. ВАЖНО: если attachment_ids НЕ переданы (поле отсутствует или multipart без файлов), текущие вложения черновика СОХРАНЯЮТСЯ — отправка с пустым телом больше не стирает уже приложенные файлы. Явный пустой массив [] очищает список. МОЖНО ПРИКРЕПИТЬ НЕСКОЛЬКО ФАЙЛОВ за один запрос — до SubmissionMaxFiles (по умолчанию 5, переменная окружения SUBMISSION_MAX_FILES): в multipart повторите файловое поле для каждого файла (-F "attachments=@a.pdf" -F "attachments=@b.pdf"; имя поля любое — сервер собирает ВСЕ файловые части), либо передайте массив из нескольких UUID в attachment_ids (JSON). Превышение лимита → 400 too_many_attachments.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID работы. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
attachment_idsопц. | string[] | Список UUID медиа-активов — итоговый набор вложений черновика (для JSON-тела). |
Пример запроса
# Вариант 1 — JSON с предзагруженными media UUID
curl -X PATCH https://api.edumentor.kz/api/v1/t/submissions/5b5b... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "attachment_ids": ["m2m2..."] }'
# Вариант 2 — multipart/form-data с самими файлами
curl -X PATCH https://api.edumentor.kz/api/v1/t/submissions/5b5b... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-F "files=@/path/to/file.pdf"Пример ответа
{
"success": true,
"data": {
"id": "5b5b...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"status": "draft",
"score": null,
"feedback": null,
"graded_by_id": null,
"graded_at": null,
"submitted_at": null,
"attachments": [
{
"id": "att1...",
"media_asset_id": "m2m2...",
"position": 0,
"filename": "essay.pdf",
"presigned_url": "https://s3.edumentor.kz/media/...&X-Amz-Signature=...",
"expires_in": 900
}
],
"created_at": "2026-05-28T10:00:00Z",
"updated_at": "2026-05-28T10:30:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 400 | invalid_request_body | Некорректное тело запроса. |
| 400 | attachment_not_in_tenant | Вложение принадлежит другой школе. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 404 | submission_not_found | Работа отсутствует или не принадлежит вызывающему. |
| 422 | submission_not_in_draft | Работа уже не в статусе черновика — редактирование запрещено. |
/api/v1/t/submissions/:id/submitОтправить работу
Студент отправляет работу на проверку с финальным списком вложений. Доступ только владельца. Сервер проверяет лимит количества файлов.
Принимается ЛИБО JSON-тело с attachment_ids (предзагруженные media UUID), ЛИБО multipart/form-data с самими файлами (любое имя поля для файловых частей) — в этом случае сервер загружает файлы и использует их как итоговый набор вложений. ВАЖНО: если attachment_ids НЕ переданы (поле отсутствует или multipart без файлов), текущие вложения черновика СОХРАНЯЮТСЯ — отправка с пустым телом больше не стирает уже приложенные файлы. Явный пустой массив [] очищает список. МОЖНО ПРИКРЕПИТЬ НЕСКОЛЬКО ФАЙЛОВ за один запрос — до SubmissionMaxFiles (по умолчанию 5, переменная окружения SUBMISSION_MAX_FILES): в multipart повторите файловое поле для каждого файла (-F "attachments=@a.pdf" -F "attachments=@b.pdf"; имя поля любое — сервер собирает ВСЕ файловые части), либо передайте массив из нескольких UUID в attachment_ids (JSON). Превышение лимита → 400 too_many_attachments.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID работы. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
attachmentsопц. | file[] | Multipart-вариант: один или НЕСКОЛЬКО файлов (до SubmissionMaxFiles, по умолчанию 5). Повторите поле для каждого файла; имя поля любое — сервер собирает все файловые части. |
attachment_idsопц. | string[] | Финальный список UUID вложений на момент отправки (для JSON-тела). Можно передать несколько UUID. |
Пример запроса
# Вариант 1 — JSON с НЕСКОЛЬКИМИ предзагруженными media UUID
curl -X POST https://api.edumentor.kz/api/v1/t/submissions/5b5b.../submit \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "attachment_ids": ["m2m2...", "m3m3...", "m4m4..."] }'
# Вариант 2 — multipart/form-data, НЕСКОЛЬКО файлов (до 5)
# повторите -F для каждого файла; имя поля любое
curl -X POST https://api.edumentor.kz/api/v1/t/submissions/5b5b.../submit \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-F "attachments=@/path/to/essay.pdf" \
-F "attachments=@/path/to/appendix.pdf" \
-F "attachments=@/path/to/photo.jpg"Пример ответа
{
"success": true,
"data": {
"id": "5b5b...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"status": "submitted",
"score": null,
"feedback": null,
"graded_by_id": null,
"graded_at": null,
"submitted_at": "2026-05-28T11:00:00Z",
"attachments": [
{
"id": "att1...",
"media_asset_id": "m2m2...",
"position": 0,
"filename": "essay.pdf",
"presigned_url": "https://s3.edumentor.kz/media/...&X-Amz-Signature=...",
"expires_in": 900
}
],
"created_at": "2026-05-28T10:00:00Z",
"updated_at": "2026-05-28T11:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 400 | invalid_request_body | Некорректное тело запроса. |
| 400 | too_many_attachments | Превышен лимит количества файлов в работе. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | schedule_window_closed | Урок задания вне окна доступа по расписанию, и правило «первой попытки» не применимо (студент не открывал работу до дедлайна). Отклонённую до дедлайна работу можно перезагрузить и после него. |
| 404 | submission_not_found | Работа отсутствует или не принадлежит вызывающему. |
| 422 | submission_not_in_draft | Работа уже не в статусе черновика — повторная отправка запрещена. |
/api/v1/t/submissions/:id/reviewПроверить работу (Принят / Не принят)
Куратор/админ выносит решение проверки. decision обязателен: accepted (Принят) или rejected (Не принят). score и feedback необязательны — допускается решение без балла. Непривилегированный куратор может проверять только своих студентов. Можно перепроверять уже проверенную работу (submitted/accepted/rejected → новое решение).
Решение НЕ влияет на прохождение курса: блок задания завершается при ОТПРАВКЕ работы (см. POST .../submit), а не при проверке. rejected НЕ откатывает завершение блока — оно лишь открывает пересдачу (следующий POST .../submissions создаёт новую попытку). На каждое решение (и accepted, и rejected) студенту отправляется уведомление; при наличии балла начисляются геймификация-очки. Легаси-статус graded больше не выставляется.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
decisionобяз. | string | Решение проверки: accepted | rejected. |
scoreопц. | string | Балл как числовая строка (в пределах max_score). Необязателен. |
feedbackопц. | string | HTML-отзыв (очищается сервером). |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/submissions/5b5b.../review \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "decision": "accepted", "score": "85", "feedback": "<p>Хорошая работа</p>" }'Пример ответа
{
"success": true,
"data": {
"id": "5b5b...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"status": "accepted",
"score": "85",
"feedback": "<p>Хорошая работа</p>",
"graded_by_id": "c1c1...",
"graded_at": "2026-05-28T12:00:00Z",
"submitted_at": "2026-05-28T11:00:00Z",
"attachments": [
{
"id": "att1...",
"media_asset_id": "m2m2...",
"position": 0,
"filename": "essay.pdf",
"presigned_url": "https://s3.edumentor.kz/media/...&X-Amz-Signature=...",
"expires_in": 900
}
],
"created_at": "2026-05-28T10:00:00Z",
"updated_at": "2026-05-28T12:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 400 | invalid_request_body | Некорректное тело запроса (например, decision не из accepted|rejected). |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_students. |
| 404 | submission_not_found | Работа отсутствует, другая школа или вне охвата куратора. |
| 422 | review_decision_required | decision не задан или не из accepted|rejected. |
| 422 | submission_not_in_gradable_state | Работа не в состоянии, допускающем проверку (например, черновик). |
| 422 | score_out_of_range | Балл вне диапазона 0..max_score задания. |
/api/v1/t/submissions/:id/reopenПереоткрыть работу
Куратор/админ возвращает проверенную работу (accepted | rejected) в статус «отправлена» для повторной проверки. Балл и комментарий сохраняются; завершение блока не меняется. Тело пустое. Идемпотентно: уже отправленная работа — 200 без изменений; черновик переоткрыть нельзя (422).
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID работы. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/submissions/5b5b.../reopen \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "5b5b...",
"assignment_id": "3c3c...",
"student_id": "8f3b...",
"status": "submitted",
"score": null,
"feedback": null,
"graded_by_id": null,
"graded_at": null,
"submitted_at": "2026-05-28T11:00:00Z",
"attachments": [
{
"id": "att1...",
"media_asset_id": "m2m2...",
"position": 0,
"filename": "essay.pdf",
"presigned_url": "https://s3.edumentor.kz/media/...&X-Amz-Signature=...",
"expires_in": 900
}
],
"created_at": "2026-05-28T10:00:00Z",
"updated_at": "2026-05-28T12:30:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_students. |
| 404 | submission_not_found | Работа отсутствует, другая школа или вне охвата куратора. |
| 422 | cannot_reopen_draft | Работа в статусе черновика — переоткрытие невозможно. |