Группы
Когорты студентов: CRUD групп, назначение куратора, управление составом, обратный поиск групп студента и массовая запись группы на курс. Все эндпоинты живут под /api/v1/t/* и требуют JWT + заголовок X-Tenant-Subdomain.
- GET
/api/v1/t/groupsСписок групп - GET
/api/v1/t/groups/:idПолучить группу - POST
/api/v1/t/groupsСоздать группу - PATCH
/api/v1/t/groups/:idИзменить группу - PUT
/api/v1/t/groups/:id/curatorНазначить куратора группы - DELETE
/api/v1/t/groups/:idУдалить группу - GET
/api/v1/t/groups/:id/membersСписок участников группы - POST
/api/v1/t/groups/:id/membersДобавить участников - DELETE
/api/v1/t/groups/:id/membersУдалить участников - GET
/api/v1/t/users/:id/groupsГруппы студента - POST
/api/v1/t/courses/:id/enroll-bulkМассовая запись группы на курс
/api/v1/t/groupsСписок групп
Постраничный список групп школы с количеством участников и гидрированным куратором. Непривилегированный куратор видит только свои группы (curator_id = он сам); назначенные другим или без куратора группы скрыты.
Конверт: { groups, total, limit, offset }. limit 1..200 (по умолчанию 50), offset >= 0. Поле logo — сырое значение (UUID медиа-ассета или URL), для формы редактирования. Поле logo_url — готовая ссылка (presigned URL, если logo — UUID), для отображения. Оба null, если логотипа нет.
Query-параметры
| Параметр | Тип | Описание |
|---|---|---|
qопц. | string | Поиск по названию группы. |
limitопц. | integer | Размер страницы (1..200). По умолчанию 50.Пример: 50 |
offsetопц. | integer | Смещение (>=0). По умолчанию 0.Пример: 0 |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/groups?limit=50&offset=0" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"groups": [
{
"id": "g1g1...",
"name": "Поток 2026",
"description": "Весенний набор",
"logo": "a1b2c3d4-0000-4000-8000-000000000000",
"logo_url": "https://minio.edumentor.kz/lms/.../logo.png?X-Amz-Signature=...",
"created_by": "c1c1...",
"curator_id": "c2c2...",
"curator_first_name": "Дана",
"curator_last_name": "Серик",
"curator_email": "curator@acme.kz",
"member_count": 0,
"created_at": "2026-05-28T09:00:00Z",
"updated_at": "2026-05-28T09:00:00Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_params | Некорректные limit/offset (вне диапазона). |
| 401 | unauthorized | Школа не определена или токен недействителен. |
/api/v1/t/groups/:idПолучить группу
Возвращает одну группу с количеством участников и гидрированным куратором. Кросс-тенантный ID отдаётся как 404. Для непривилегированного куратора группа вне его охвата или без куратора схлопывается в 404.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID группы. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/groups/g1g1... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "g1g1...",
"name": "Поток 2026",
"description": "Весенний набор",
"logo": "a1b2c3d4-0000-4000-8000-000000000000",
"logo_url": "https://minio.edumentor.kz/lms/.../logo.png?X-Amz-Signature=...",
"created_by": "c1c1...",
"curator_id": "c2c2...",
"curator_first_name": "Дана",
"curator_last_name": "Серик",
"curator_email": "curator@acme.kz",
"member_count": 12,
"created_at": "2026-05-28T09:00:00Z",
"updated_at": "2026-05-28T09:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 404 | group_not_found | Группа отсутствует, другая школа или вне охвата куратора. |
/api/v1/t/groupsСоздать группу
Создаёт новую группу (когорту). 201 Created. Создатель берётся из JWT.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
nameобяз. | string | Название группы (1..120). |
descriptionопц. | string | Описание (до 2000 символов). |
logoопц. | string | Логотип: UUID подтверждённого медиа-ассета (kind=image) ИЛИ URL картинки. Опустите — без логотипа. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/groups \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "name": "Поток 2026", "description": "Весенний набор" }'Пример ответа
{
"success": true,
"data": {
"id": "g1g1...",
"name": "Поток 2026",
"description": "Весенний набор",
"logo": null,
"logo_url": null,
"created_by": "c1c1...",
"curator_id": null,
"member_count": 0,
"created_at": "2026-05-28T09:00:00Z",
"updated_at": "2026-05-28T09:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_request_body | Некорректное тело запроса. |
| 400 | group_name_required | Название группы обязательно. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_groups. |
/api/v1/t/groups/:idИзменить группу
Частичное обновление названия/описания/логотипа группы. Переданные поля заменяют значения; опущенные — не трогаются.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID группы. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
nameопц. | string | Новое название (1..120). |
descriptionопц. | string | Новое описание (до 2000). |
logoопц. | string | Логотип: UUID медиа-ассета или URL картинки. Пустая строка "" — снять логотип (NULL). Опустите — оставить как есть. |
Пример запроса
curl -X PATCH https://api.edumentor.kz/api/v1/t/groups/g1g1... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "name": "Поток 2026 (обновлён)" }'Пример ответа
{
"success": true,
"data": {
"id": "g1g1...",
"name": "Поток 2026 (обновлён)",
"description": "Весенний набор",
"logo": "a1b2c3d4-0000-4000-8000-000000000000",
"logo_url": "https://minio.edumentor.kz/lms/.../logo.png?X-Amz-Signature=...",
"created_by": "c1c1...",
"curator_id": "c2c2...",
"member_count": 12,
"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 | Некорректное тело запроса. |
| 400 | group_name_required | Название группы не может быть пустым. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_groups. |
| 404 | group_not_found | Группа отсутствует или принадлежит другой школе. |
/api/v1/t/groups/:id/curatorНазначить куратора группы
Назначает (или снимает) куратора группы. curator_id = null означает «снять куратора». Целевой пользователь проверяется на роль и принадлежность школе.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
curator_idопц. | uuid | ID куратора. null — снять назначение. |
Пример запроса
curl -X PUT https://api.edumentor.kz/api/v1/t/groups/g1g1.../curator \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "curator_id": "c2c2..." }'Пример ответа
{
"success": true,
"data": {
"id": "g1g1...",
"name": "Поток 2026",
"description": "Весенний набор",
"logo": "a1b2c3d4-0000-4000-8000-000000000000",
"logo_url": "https://minio.edumentor.kz/lms/.../logo.png?X-Amz-Signature=...",
"created_by": "c1c1...",
"curator_id": "c2c2...",
"curator_first_name": "Дана",
"curator_last_name": "Серик",
"curator_email": "curator@acme.kz",
"member_count": 12,
"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 | Некорректное тело запроса. |
| 400 | curator_not_eligible | Целевой пользователь не подходит на роль куратора (роль/школа). |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_groups. |
| 404 | group_not_found | Группа отсутствует или принадлежит другой школе. |
/api/v1/t/groups/:idУдалить группу
Мягко удаляет группу вместе с её членствами (каскад внутри транзакции). Возвращает { deleted: true }.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID группы. |
Пример запроса
curl -X DELETE https://api.edumentor.kz/api/v1/t/groups/g1g1... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": { "deleted": true }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_groups. |
| 404 | group_not_found | Группа отсутствует или принадлежит другой школе. |
/api/v1/t/groups/:id/membersСписок участников группы
Постраничный список участников группы с email и именем студента. Конверт: { members, total, limit, offset }. Для непривилегированного куратора группа вне охвата схлопывается в 404.
limit 1..200 (по умолчанию 50), offset >= 0.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID группы. |
Query-параметры
| Параметр | Тип | Описание |
|---|---|---|
limitопц. | integer | Размер страницы (1..200). По умолчанию 50.Пример: 50 |
offsetопц. | integer | Смещение (>=0). По умолчанию 0.Пример: 0 |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/groups/g1g1.../members?limit=50&offset=0" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"members": [
{
"membership_id": "mm1...",
"student_id": "8f3b...",
"email": "student@acme.kz",
"first_name": "Айгерим",
"last_name": "Нурлан",
"is_active": true,
"joined_at": "2026-05-28T09:30:00Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID в пути не является UUID. |
| 400 | invalid_params | Некорректные limit/offset. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 404 | group_not_found | Группа отсутствует, другая школа или вне охвата куратора. |
/api/v1/t/groups/:id/membersДобавить участников
Добавляет список студентов в группу (частичный успех). Каждый необработанный студент попадает в skipped[] с причиной: already_member | user_not_found | wrong_tenant.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID группы. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
student_idsобяз. | string[] | Массив UUID студентов (от 1 до 500). |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/groups/g1g1.../members \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "student_ids": ["8f3b...", "7a2c..."] }'Пример ответа
{
"success": true,
"data": {
"added": ["8f3b..."],
"skipped": [{ "student_id": "7a2c...", "reason": "already_member" }]
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID группы в пути не является UUID. |
| 400 | invalid_request_body | Некорректное тело запроса. |
| 400 | empty_member_list | Список студентов пуст. |
| 400 | too_many_members | Превышен лимит количества студентов в одном запросе. |
| 400 | cross_tenant_student | Студент принадлежит другой школе. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_groups. |
| 404 | group_not_found | Группа отсутствует или принадлежит другой школе. |
/api/v1/t/groups/:id/membersУдалить участников
Массово удаляет студентов из группы. student_ids передаются в теле запроса. Возвращает { removed: true }.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID группы. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
student_idsобяз. | string[] | Массив UUID студентов для удаления (от 1 до 500). |
Пример запроса
curl -X DELETE https://api.edumentor.kz/api/v1/t/groups/g1g1.../members \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "student_ids": ["8f3b..."] }'Пример ответа
{
"success": true,
"data": { "removed": true }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID группы в пути не является UUID. |
| 400 | invalid_request_body | Некорректное тело запроса. |
| 400 | empty_member_list | Список студентов пуст. |
| 400 | too_many_members | Превышен лимит количества студентов в одном запросе. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_groups. |
| 404 | group_not_found | Группа отсутствует или принадлежит другой школе. |
| 404 | member_not_found | Один из переданных студентов не является участником группы. |
/api/v1/t/users/:id/groupsГруппы студента
Обратный поиск: в каких группах состоит студент. Кросс-тенантный student_id даёт пустой список (приватность по паритету). Непривилегированный куратор видит только пересечение со своими группами.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID студента. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/users/8f3b.../groups \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"groups": [
{
"group_id": "g1g1...",
"name": "Поток 2026",
"description": "Весенний набор",
"joined_at": "2026-05-28T09:30:00Z"
}
]
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | ID студента в пути не является UUID. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_students. |
/api/v1/t/courses/:id/enroll-bulkМассовая запись группы на курс
Записывает на курс всю группу (group_id) ИЛИ явный список студентов (student_ids) — ровно одно из полей. Частичный успех: каждый необработанный студент попадает в skipped[] с причиной (user_not_found | wrong_tenant). Уже записанные считаются как enrolled.
Отличается от POST /api/v1/t/courses/:id/enroll/bulk (через слэш) из раздела «Записи на курсы»: другой URL (дефис) и другое право (manage_groups вместо manage_students). Всегда 200, даже если часть пропущена. Total = enrolled + skipped.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | ID курса. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
group_idопц. | uuid | ID группы для записи. Взаимоисключающе с student_ids. |
student_idsопц. | string[] | Явный список UUID студентов (1..500). Взаимоисключающе с group_id. |
Пример запроса
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 '{ "group_id": "g1g1..." }'Пример ответа
{
"success": true,
"data": {
"enrolled": ["8f3b...", "7a2c..."],
"skipped": [],
"total": 2
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_course_id | ID курса в пути не является UUID. |
| 400 | invalid_request_body | Некорректное тело запроса. |
| 400 | invalid_bulk_enroll_payload | Нарушено правило «ровно одно из group_id / student_ids». |
| 400 | too_many_members | Превышен лимит количества студентов. |
| 401 | unauthorized | Школа не определена или токен недействителен. |
| 403 | permission_denied | Нет права manage_groups. |
| 404 | course_not_found | Курс отсутствует, в черновике или принадлежит другой школе. |
| 404 | group_not_found | Группа отсутствует или принадлежит другой школе. |