Записи на курсы
Самостоятельная запись студента на курс, кураторский список и CSV-экспорт записей, массовая запись, снятие с курса, список «мои курсы», отметка о прохождении блока. Все эндпоинты живут под /api/v1/t/* и требуют JWT + заголовок X-Tenant-Subdomain.
- POST
/api/v1/t/courses/:id/enrollЗаписаться на курс - GET
/api/v1/t/courses/:id/enrollmentsСписок записей на курс - GET
/api/v1/t/courses/:id/enrollments/export.csvЭкспорт записей в CSV - POST
/api/v1/t/courses/:id/enroll/bulkМассовая запись студентов - DELETE
/api/v1/t/courses/:id/enrollments/:student_idСнять студента с курса - GET
/api/v1/t/me/coursesМои курсы - GET
/api/v1/t/courses/:id/my-completionsПройденные блоки курса - POST
/api/v1/t/blocks/:id/completeОтметить блок пройденным
/api/v1/t/courses/:id/enrollЗаписаться на курс
Записывает текущего студента на опубликованный курс. Тело запроса пустое — студент берётся из JWT, передать чужой student_id невозможно. Идемпотентно: при уже активной записи возвращается 200 с существующей строкой.
201 Created — создана новая запись; 200 OK — запись уже существовала или была восстановлена. Курс, входящий в траекторию обучения и ещё не разблокированный для студента, отклоняется с 409 course_locked.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID курса. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/courses/2b1a.../enroll \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "9c1f...",
"student_id": "8f3b...",
"course_id": "2b1a...",
"enrolled_at": "2026-05-28T10:00:00Z",
"enrolled_by": null,
"last_accessed_block_id": null
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_course_id | ID курса в пути не является UUID. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | tenant_required | Школа (тенант) не определена для запроса. |
| 404 | course_not_found | Курс отсутствует, в черновике или принадлежит другой школе (схлопывание для приватности). |
| 409 | course_locked | Курс входит в траекторию обучения и ещё не разблокирован для этого студента. |
/api/v1/t/courses/:id/enrollmentsСписок записей на курс
Кураторско-административный список студентов, записанных на курс, с прогрессом, счётчиками по статусам и средним прогрессом. Непривилегированный куратор видит только своих студентов.
Пагинация: page (с 1), limit (по умолчанию 20, ограничен 1..100). Счётчики counts и avg_progress считаются под фильтром q, но игнорируют фильтр status.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID курса. |
Query-параметры
| Параметр | Тип | Описание |
|---|---|---|
qопц. | string | Поиск по имени/email студента. |
statusопц. | string | Фильтр по статусу: active | risk | done | notstarted. |
pageопц. | integer | Номер страницы (с 1). По умолчанию 1.Пример: 1 |
limitопц. | integer | Размер страницы (1..100). По умолчанию 20.Пример: 20 |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/courses/2b1a.../enrollments?page=1&limit=20" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"enrollments": [
{
"enrollment_id": "9c1f...",
"student_id": "8f3b...",
"student_email": "student@acme.kz",
"student_first_name": "Айгерим",
"student_last_name": "Нурлан",
"enrolled_at": "2026-05-28T10:00:00Z",
"enrolled_by": null,
"blocks_completed": 3,
"total_blocks": 10,
"pct": 30,
"last_accessed_at": "2026-05-28T11:00:00Z",
"completed_at": null
}
],
"total": 1,
"page": 1,
"limit": 20,
"counts": { "all": 1, "active": 1, "risk": 0, "done": 0, "notstarted": 0 },
"avg_progress": 30
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_course_id | ID курса в пути не является UUID. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | permission_denied | Нет права manage_students (или не определена школа). |
| 404 | course_not_found | Курс отсутствует, в черновике или принадлежит другой школе. |
/api/v1/t/courses/:id/enrollments/export.csvЭкспорт записей в CSV
Выгружает все активные записи на курс в CSV (без пагинации и фильтров — «весь курс»). Та же проверка прав, что и у списка; непривилегированный куратор получает только своих студентов.
Ответ — text/csv; charset=utf-8 с BOM в начале (чтобы Excel на Windows корректно открыл кириллицу). Имя файла отдаётся в Content-Disposition. Заголовки колонок: ФИО, Email, Дата записи, Прогресс %, Завершено блоков, Всего блоков, Статус, Последняя активность. Значения статуса: «активен» | «в зоне риска» | «завершил» | «не начал».
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID курса. |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/courses/2b1a.../enrollments/export.csv" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" -o enrollments.csvПример ответа
Content-Type: text/csv; charset=utf-8
ФИО,Email,Дата записи,Прогресс %,Завершено блоков,Всего блоков,Статус,Последняя активность
Айгерим Нурлан,student@acme.kz,2026-05-28,30,3,10,активен,2026-05-28 11:00Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_course_id | ID курса в пути не является UUID. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | permission_denied | Нет права manage_students. |
| 404 | course_not_found | Курс отсутствует, в черновике или принадлежит другой школе. |
/api/v1/t/courses/:id/enroll/bulkМассовая запись студентов
Записывает список студентов на курс одним запросом (частичный успех). Каждый необработанный студент попадает в skipped[] с причиной. Для непривилегированного куратора студенты вне его охвата пропускаются с причиной not_in_scope.
Всегда 200, даже если часть пропущена. Причины пропуска: already_enrolled | user_not_found | wrong_tenant | course_not_published. Существует второй массовый эндпоинт POST /api/v1/t/courses/:id/enroll-bulk (через дефис) с правом manage_groups — см. раздел «Группы».
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID курса. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
student_idsобяз. | string[] | Массив UUID студентов (от 1 до 500). |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/courses/2b1a.../enroll/bulk \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "student_ids": ["8f3b...", "7a2c..."] }'Пример ответа
{
"success": true,
"data": {
"enrolled": ["8f3b..."],
"skipped": [{ "student_id": "7a2c...", "reason": "already_enrolled" }],
"enrolled_count": 1,
"skipped_count": 1
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_body | Некорректное тело запроса (нарушены binding-правила). |
| 400 | student_ids_cap_exceeded | Превышен лимит количества student_ids. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | permission_denied | Нет права manage_students. |
| 404 | course_not_found | Курс отсутствует, в черновике или принадлежит другой школе. |
/api/v1/t/courses/:id/enrollments/:student_idСнять студента с курса
Снимает активную запись студента с курса (административное действие). Непривилегированный куратор может снять только своего студента; цель вне охвата схлопывается в 404.
204 No Content при успехе.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID курса. |
student_idобяз. | uuid | ID студента. |
Пример запроса
curl -X DELETE https://api.edumentor.kz/api/v1/t/courses/2b1a.../enrollments/8f3b... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
HTTP/1.1 204 No ContentОшибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_course_id | ID курса в пути не является UUID. |
| 400 | invalid_student_id | ID студента в пути не является UUID. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | permission_denied | Нет права manage_students. |
| 404 | enrollment_not_found | Активная запись не найдена (или вне охвата куратора / другая школа). |
/api/v1/t/me/coursesМои курсы
Постраничный список курсов, на которые записан текущий студент, с вычисленным процентом прохождения, указателем на последний открытый блок и временем завершения. Возвращает массив строк StudentCourseListItem.
Курсы с активной записью, но снятые с публикации, всё равно показываются в этом списке. Поле auth_has_access в каждой строке всегда true — каждая строка соответствует курсу, на который студент записан.
Query-параметры
| Параметр | Тип | Описание |
|---|---|---|
limitопц. | integer | Размер страницы. По умолчанию 20.Пример: 20 |
offsetопц. | integer | Смещение. По умолчанию 0.Пример: 0 |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/me/courses?limit=20&offset=0" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": [
{
"course_id": "2b1a...",
"course_title": "Основы Go",
"course_status": "published",
"cover_image_id": "4d2e...",
"enrollment_id": "9c1f...",
"enrolled_at": "2026-05-28T10:00:00Z",
"last_accessed_block_id": "1a1a...",
"last_accessed_at": "2026-05-28T11:00:00Z",
"blocks_completed": 3,
"total_blocks": 10,
"pct": 30,
"completed_at": null,
"auth_has_access": true
}
]
}Ошибки
| Код | error | Когда |
|---|---|---|
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | tenant_required | Школа (тенант) не определена для запроса. |
/api/v1/t/courses/:id/my-completionsПройденные блоки курса
Возвращает ID всех блоков курса, для которых у текущего студента есть отметка о прохождении. Владелец берётся из JWT — student_id передать нельзя. Используется фронтендом для постоянных галочек «уже пройдено».
Пустой массив block_ids — нормальная ситуация.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID курса. |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/courses/2b1a.../my-completions" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"block_ids": ["1a1a...", "1b1b..."]
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_course_id | ID курса в пути не является UUID. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | tenant_required | Школа (тенант) не определена для запроса. |
| 404 | course_not_found | Курс отсутствует, не опубликован, другая школа или нет активной записи (схлопывание). |
/api/v1/t/blocks/:id/completeОтметить блок пройденным
Помечает блок курса как пройденный текущим студентом (ручное прохождение текстовых/видео/PDF-блоков). Владелец берётся из JWT — отметить блок за другого студента структурно невозможно. Идемпотентно.
Блоки типа quiz_ref завершаются через прохождение теста, а не этим эндпоинтом (400). Для блока assignment_ref требуется отправленная/проверенная работа, иначе 422 assignment_not_submitted.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID блока контента. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/blocks/1a1a.../complete \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "5f5f...",
"student_id": "8f3b...",
"block_id": "1a1a...",
"enrollment_id": "9c1f...",
"source": "manual",
"completed_at": "2026-05-28T11:30:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_block_id | ID блока в пути не является UUID. |
| 400 | block_type_completed_via_domain | Блок типа quiz_ref/assignment_ref завершается через свой домен, а не вручную. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | tenant_required | Школа (тенант) не определена для запроса. |
| 404 | block_not_found | Блок не в курсе с активной записью (или другая школа) — схлопывание. |
| 422 | assignment_not_submitted | Блок assignment_ref: нет отправленной или проверенной работы. |