TenantAdminCurator

Группы

Когорты студентов: CRUD групп, назначение куратора, управление составом, обратный поиск групп студента и массовая запись группы на курс. Все эндпоинты живут под /api/v1/t/* и требуют JWT + заголовок X-Tenant-Subdomain.

GET/api/v1/t/groups

Список групп

TenantAdminCurator JWT

Постраничный список групп школы с количеством участников и гидрированным куратором. Непривилегированный куратор видит только свои группы (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
curl "https://api.edumentor.kz/api/v1/t/groups?limit=50&offset=0" \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme"

Пример ответа

200 OK · application/json
{
  "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Когда
400invalid_paramsНекорректные limit/offset (вне диапазона).
401unauthorizedШкола не определена или токен недействителен.
GET/api/v1/t/groups/:id

Получить группу

TenantAdminCurator JWT

Возвращает одну группу с количеством участников и гидрированным куратором. Кросс-тенантный ID отдаётся как 404. Для непривилегированного куратора группа вне его охвата или без куратора схлопывается в 404.

Параметры пути

ПараметрТипОписание
idобяз.uuidID группы.

Пример запроса

cURL
curl https://api.edumentor.kz/api/v1/t/groups/g1g1... \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme"

Пример ответа

200 OK · application/json
{
  "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Когда
400invalid_id_formatID в пути не является UUID.
401unauthorizedШкола не определена или токен недействителен.
404group_not_foundГруппа отсутствует, другая школа или вне охвата куратора.
POST/api/v1/t/groups

Создать группу

AdminCurator JWT право: manage_groups

Создаёт новую группу (когорту). 201 Created. Создатель берётся из JWT.

Тело запроса

ПараметрТипОписание
nameобяз.stringНазвание группы (1..120).
descriptionопц.stringОписание (до 2000 символов).
logoопц.stringЛоготип: UUID подтверждённого медиа-ассета (kind=image) ИЛИ URL картинки. Опустите — без логотипа.

Пример запроса

cURL
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": "Весенний набор" }'

Пример ответа

200 OK · application/json
{
  "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Когда
400invalid_request_bodyНекорректное тело запроса.
400group_name_requiredНазвание группы обязательно.
401unauthorizedШкола не определена или токен недействителен.
403permission_deniedНет права manage_groups.
PATCH/api/v1/t/groups/:id

Изменить группу

AdminCurator JWT право: manage_groups

Частичное обновление названия/описания/логотипа группы. Переданные поля заменяют значения; опущенные — не трогаются.

Параметры пути

ПараметрТипОписание
idобяз.uuidID группы.

Тело запроса

ПараметрТипОписание
nameопц.stringНовое название (1..120).
descriptionопц.stringНовое описание (до 2000).
logoопц.stringЛоготип: UUID медиа-ассета или URL картинки. Пустая строка "" — снять логотип (NULL). Опустите — оставить как есть.

Пример запроса

cURL
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 (обновлён)" }'

Пример ответа

200 OK · application/json
{
  "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Когда
400invalid_id_formatID в пути не является UUID.
400invalid_request_bodyНекорректное тело запроса.
400group_name_requiredНазвание группы не может быть пустым.
401unauthorizedШкола не определена или токен недействителен.
403permission_deniedНет права manage_groups.
404group_not_foundГруппа отсутствует или принадлежит другой школе.
PUT/api/v1/t/groups/:id/curator

Назначить куратора группы

AdminCurator JWT право: manage_groups

Назначает (или снимает) куратора группы. curator_id = null означает «снять куратора». Целевой пользователь проверяется на роль и принадлежность школе.

Тело запроса

ПараметрТипОписание
curator_idопц.uuidID куратора. null — снять назначение.

Пример запроса

cURL
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..." }'

Пример ответа

200 OK · application/json
{
  "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Когда
400invalid_id_formatID в пути не является UUID.
400invalid_request_bodyНекорректное тело запроса.
400curator_not_eligibleЦелевой пользователь не подходит на роль куратора (роль/школа).
401unauthorizedШкола не определена или токен недействителен.
403permission_deniedНет права manage_groups.
404group_not_foundГруппа отсутствует или принадлежит другой школе.
DELETE/api/v1/t/groups/:id

Удалить группу

AdminCurator JWT право: manage_groups

Мягко удаляет группу вместе с её членствами (каскад внутри транзакции). Возвращает { deleted: true }.

Параметры пути

ПараметрТипОписание
idобяз.uuidID группы.

Пример запроса

cURL
curl -X DELETE https://api.edumentor.kz/api/v1/t/groups/g1g1... \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme"

Пример ответа

200 OK · application/json
{
  "success": true,
  "data": { "deleted": true }
}

Ошибки

КодerrorКогда
400invalid_id_formatID в пути не является UUID.
401unauthorizedШкола не определена или токен недействителен.
403permission_deniedНет права manage_groups.
404group_not_foundГруппа отсутствует или принадлежит другой школе.
GET/api/v1/t/groups/:id/members

Список участников группы

TenantAdminCurator JWT

Постраничный список участников группы с email и именем студента. Конверт: { members, total, limit, offset }. Для непривилегированного куратора группа вне охвата схлопывается в 404.

limit 1..200 (по умолчанию 50), offset >= 0.

Параметры пути

ПараметрТипОписание
idобяз.uuidID группы.

Query-параметры

ПараметрТипОписание
limitопц.integerРазмер страницы (1..200). По умолчанию 50.Пример: 50
offsetопц.integerСмещение (>=0). По умолчанию 0.Пример: 0

Пример запроса

cURL
curl "https://api.edumentor.kz/api/v1/t/groups/g1g1.../members?limit=50&offset=0" \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme"

Пример ответа

200 OK · application/json
{
  "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Когда
400invalid_id_formatID в пути не является UUID.
400invalid_paramsНекорректные limit/offset.
401unauthorizedШкола не определена или токен недействителен.
404group_not_foundГруппа отсутствует, другая школа или вне охвата куратора.
POST/api/v1/t/groups/:id/members

Добавить участников

AdminCurator JWT право: manage_groups

Добавляет список студентов в группу (частичный успех). Каждый необработанный студент попадает в skipped[] с причиной: already_member | user_not_found | wrong_tenant.

Параметры пути

ПараметрТипОписание
idобяз.uuidID группы.

Тело запроса

ПараметрТипОписание
student_idsобяз.string[]Массив UUID студентов (от 1 до 500).

Пример запроса

cURL
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..."] }'

Пример ответа

200 OK · application/json
{
  "success": true,
  "data": {
    "added": ["8f3b..."],
    "skipped": [{ "student_id": "7a2c...", "reason": "already_member" }]
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatID группы в пути не является UUID.
400invalid_request_bodyНекорректное тело запроса.
400empty_member_listСписок студентов пуст.
400too_many_membersПревышен лимит количества студентов в одном запросе.
400cross_tenant_studentСтудент принадлежит другой школе.
401unauthorizedШкола не определена или токен недействителен.
403permission_deniedНет права manage_groups.
404group_not_foundГруппа отсутствует или принадлежит другой школе.
DELETE/api/v1/t/groups/:id/members

Удалить участников

AdminCurator JWT право: manage_groups

Массово удаляет студентов из группы. student_ids передаются в теле запроса. Возвращает { removed: true }.

Параметры пути

ПараметрТипОписание
idобяз.uuidID группы.

Тело запроса

ПараметрТипОписание
student_idsобяз.string[]Массив UUID студентов для удаления (от 1 до 500).

Пример запроса

cURL
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..."] }'

Пример ответа

200 OK · application/json
{
  "success": true,
  "data": { "removed": true }
}

Ошибки

КодerrorКогда
400invalid_id_formatID группы в пути не является UUID.
400invalid_request_bodyНекорректное тело запроса.
400empty_member_listСписок студентов пуст.
400too_many_membersПревышен лимит количества студентов в одном запросе.
401unauthorizedШкола не определена или токен недействителен.
403permission_deniedНет права manage_groups.
404group_not_foundГруппа отсутствует или принадлежит другой школе.
404member_not_foundОдин из переданных студентов не является участником группы.
GET/api/v1/t/users/:id/groups

Группы студента

AdminCurator JWT право: manage_students

Обратный поиск: в каких группах состоит студент. Кросс-тенантный student_id даёт пустой список (приватность по паритету). Непривилегированный куратор видит только пересечение со своими группами.

Параметры пути

ПараметрТипОписание
idобяз.uuidID студента.

Пример запроса

cURL
curl https://api.edumentor.kz/api/v1/t/users/8f3b.../groups \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme"

Пример ответа

200 OK · application/json
{
  "success": true,
  "data": {
    "groups": [
      {
        "group_id": "g1g1...",
        "name": "Поток 2026",
        "description": "Весенний набор",
        "joined_at": "2026-05-28T09:30:00Z"
      }
    ]
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatID студента в пути не является UUID.
401unauthorizedШкола не определена или токен недействителен.
403permission_deniedНет права manage_students.
POST/api/v1/t/courses/:id/enroll-bulk

Массовая запись группы на курс

AdminCurator JWT право: manage_groups

Записывает на курс всю группу (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обяз.uuidID курса.

Тело запроса

ПараметрТипОписание
group_idопц.uuidID группы для записи. Взаимоисключающе с student_ids.
student_idsопц.string[]Явный список UUID студентов (1..500). Взаимоисключающе с group_id.

Пример запроса

cURL
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..." }'

Пример ответа

200 OK · application/json
{
  "success": true,
  "data": {
    "enrolled": ["8f3b...", "7a2c..."],
    "skipped": [],
    "total": 2
  }
}

Ошибки

КодerrorКогда
400invalid_course_idID курса в пути не является UUID.
400invalid_request_bodyНекорректное тело запроса.
400invalid_bulk_enroll_payloadНарушено правило «ровно одно из group_id / student_ids».
400too_many_membersПревышен лимит количества студентов.
401unauthorizedШкола не определена или токен недействителен.
403permission_deniedНет права manage_groups.
404course_not_foundКурс отсутствует, в черновике или принадлежит другой школе.
404group_not_foundГруппа отсутствует или принадлежит другой школе.