Пользователи
Управление пользователями школы (ученики, кураторы, администраторы): создание, список, роли, права кураторов, массовые операции и самообслуживание профиля `/me`. Все эндпоинты тенант-скоупные и требуют заголовка субдомена.
- POST
/api/v1/t/usersСоздать пользователя - GET
/api/v1/t/usersСписок пользователей - POST
/api/v1/t/users/bulk-activateМассовая активация - POST
/api/v1/t/users/bulk-deactivateМассовая деактивация - GET
/api/v1/t/users/:idПользователь по ID - PUT
/api/v1/t/users/:idОбновить пользователя - DELETE
/api/v1/t/users/:idУдалить пользователя - PUT
/api/v1/t/users/:id/roleИзменить роль - GET
/api/v1/t/users/:id/permissionsПрава куратора - PUT
/api/v1/t/users/:id/permissionsОбновить права куратора - GET
/api/v1/t/me/profileМой профиль - PATCH
/api/v1/t/me/profileОбновить мой профиль - POST
/api/v1/t/me/change-passwordСменить пароль
/api/v1/t/usersСоздать пользователя
Создаёт пользователя в текущей школе. Доступно админу и кураторам с правом manage_students. Email приводится к нижнему регистру и должен быть уникален в рамках школы. Для роли curator автоматически создаётся набор прав по умолчанию.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
emailобяз. | string | Email пользователя (валидный email). |
passwordобяз. | string | Пароль (минимум 8 символов). |
first_nameобяз. | string | Имя. |
last_nameобяз. | string | Фамилия. |
roleобяз. | string | Роль: admin, curator или student.Пример: student |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/users \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{
"email": "student@acme.kz",
"password": "super-secret",
"first_name": "Дамир",
"last_name": "Сериков",
"role": "student"
}'Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"tenant_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email": "student@acme.kz",
"first_name": "Дамир",
"last_name": "Сериков",
"avatar": null,
"role": "student",
"is_active": true,
"last_active_at": null,
"created_at": "2026-05-28T10:00:00Z",
"updated_at": "2026-05-28T10:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | email already taken | Пользователь с таким email уже существует в этой школе. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Нет права manage_students. |
/api/v1/t/usersСписок пользователей
Постранично возвращает пользователей школы с фильтрами по роли, статусу и поисковой строке. Доступно админу и кураторам с правом manage_students. Непривилегированные кураторы видят только своих учеников (my_students).
Параметр role принимает одно значение (?role=student) или несколько (?role=curator,admin либо повтор ?role=curator&role=admin). Для списка учеников каждая строка дополняется полем group_count и массивом groups (брифы групп: group_id, name, logo), упорядоченным по дате вступления.
Query-параметры
| Параметр | Тип | Описание |
|---|---|---|
pageопц. | integer | Номер страницы (>= 1). По умолчанию 1. |
limitопц. | integer | Размер страницы (1–200). По умолчанию 20. |
roleопц. | string | Фильтр по роли — одно или несколько значений.Пример: student |
statusопц. | string | Фильтр по статусу: active или inactive.Пример: active |
qопц. | string | Поиск по имени/email. |
Пример запроса
curl "https://api.edumentor.kz/api/v1/t/users?role=student&status=active&page=1&limit=20" \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"users": [
{
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"tenant_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email": "student@acme.kz",
"first_name": "Дамир",
"last_name": "Сериков",
"avatar": null,
"role": "student",
"is_active": true,
"last_active_at": "2026-05-28T09:50:00Z",
"group_count": 2,
"groups": [
{ "group_id": "g1g1...", "name": "Поток 2026", "logo": "a1b2c3d4-0000-4000-8000-000000000000" },
{ "group_id": "g2g2...", "name": "Группа B", "logo": null }
],
"created_at": "2026-05-20T10:00:00Z",
"updated_at": "2026-05-28T09:50:00Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_params | Некорректные page/limit (page < 1 или limit вне диапазона 1–200). |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Нет права manage_students. |
/api/v1/t/users/bulk-activateМассовая активация
Включает (is_active=true) сразу несколько пользователей по списку идентификаторов. Идемпотентно: уже активные пропускаются. Доступно админу и кураторам с правом manage_students.
Если в ids присутствует собственный идентификатор вызывающего — отклоняется ВЕСЬ запрос (400 cannot_modify_self). Для непривилегированного куратора чужие ученики попадают в skipped_details с reason not_in_scope.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
idsобяз. | string[] | Список UUID пользователей (1–100). |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/users/bulk-activate \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "ids": ["8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d"] }'Пример ответа
{
"success": true,
"data": {
"affected": 1,
"skipped": 0
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_params | Тело запроса не прошло валидацию (пустой список или > 100 идентификаторов). |
| 400 | cannot_modify_self | В списке присутствует собственный идентификатор вызывающего. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Нет права manage_students. |
/api/v1/t/users/bulk-deactivateМассовая деактивация
Выключает (is_active=false) сразу несколько пользователей по списку идентификаторов. Симметрично массовой активации: идемпотентно, тенант-скоупно, с защитой от самодеактивации.
Если в ids присутствует собственный идентификатор вызывающего — отклоняется ВЕСЬ запрос (400 cannot_modify_self).
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
idsобяз. | string[] | Список UUID пользователей (1–100). |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/users/bulk-deactivate \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "ids": ["8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d"] }'Пример ответа
{
"success": true,
"data": {
"affected": 1,
"skipped": 0,
"skipped_details": [
{ "id": "2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e", "reason": "not_in_scope" }
]
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_params | Тело запроса не прошло валидацию (пустой список или > 100 идентификаторов). |
| 400 | cannot_modify_self | В списке присутствует собственный идентификатор вызывающего. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Нет права manage_students. |
/api/v1/t/users/:idПользователь по ID
Возвращает одного пользователя школы (с правами куратора, если применимо). Доступно админу и кураторам с правом manage_students. Для непривилегированного куратора ученик вне его my_students возвращает 404 — тот же отпечаток, что и отсутствующий.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор пользователя. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/users/8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"tenant_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email": "student@acme.kz",
"first_name": "Дамир",
"last_name": "Сериков",
"avatar": null,
"role": "student",
"is_active": true,
"last_active_at": "2026-05-28T09:50:00Z",
"group_count": 2,
"groups": [
{ "group_id": "g1g1...", "name": "Поток 2026", "logo": "a1b2c3d4-0000-4000-8000-000000000000" },
{ "group_id": "g2g2...", "name": "Группа B", "logo": null }
],
"created_at": "2026-05-20T10:00:00Z",
"updated_at": "2026-05-28T09:50:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid id format | Идентификатор не является корректным UUID. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Нет права manage_students. |
| 404 | user_not_found | Пользователь не найден либо вне области видимости куратора. |
/api/v1/t/users/:idОбновить пользователя
Частично обновляет профиль пользователя: имя, фамилию, аватар, флаг активности. Доступно только роли admin.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор пользователя. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
first_nameопц. | string | Имя (минимум 2 символа). |
last_nameопц. | string | Фамилия (минимум 2 символа). |
avatarопц. | string | URL аватара или UUID медиа-ассета (изображение). Для UUID на чтение возвращается готовый presigned-URL. |
is_activeопц. | boolean | Активность учётной записи. |
Пример запроса
curl -X PUT https://api.edumentor.kz/api/v1/t/users/8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "first_name": "Дамир", "is_active": false }'Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"tenant_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email": "student@acme.kz",
"first_name": "Дамир",
"last_name": "Сериков",
"avatar": null,
"role": "student",
"is_active": false,
"created_at": "2026-05-20T10:00:00Z",
"updated_at": "2026-05-28T12:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid id format | Идентификатор не является корректным UUID. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Недостаточно прав — требуется роль администратора. |
| 404 | user not found | Пользователь с таким идентификатором не найден в школе. |
/api/v1/t/users/:idУдалить пользователя
Мягко удаляет пользователя школы (проставляет deleted_at). Доступно только роли admin. Нельзя удалить самого себя.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор пользователя. |
Пример запроса
curl -X DELETE https://api.edumentor.kz/api/v1/t/users/8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": { "deleted": true }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid id format | Идентификатор не является корректным UUID. |
| 400 | cannot delete yourself | Попытка удалить собственную учётную запись. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Недостаточно прав — требуется роль администратора. |
/api/v1/t/users/:id/roleИзменить роль
Меняет роль пользователя на admin, curator или student. Доступно только роли admin. При назначении роли curator автоматически создаётся набор прав по умолчанию, если его ещё нет.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор пользователя. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
roleобяз. | string | Новая роль: admin, curator или student.Пример: curator |
Пример запроса
curl -X PUT https://api.edumentor.kz/api/v1/t/users/8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d/role \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "role": "curator" }'Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"tenant_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email": "student@acme.kz",
"first_name": "Дамир",
"last_name": "Сериков",
"role": "curator",
"is_active": true,
"permissions": {
"id": "9c0d1e2f-3a4b-5c6d-7e8f-9a0b1c2d3e4f",
"user_id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"can_manage_courses": true,
"can_manage_students": true,
"can_manage_groups": true,
"can_view_analytics": false,
"can_manage_exams": false,
"created_at": "2026-05-28T12:30:00Z",
"updated_at": "2026-05-28T12:30:00Z"
},
"created_at": "2026-05-20T10:00:00Z",
"updated_at": "2026-05-28T12:30:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid id format | Идентификатор не является корректным UUID. |
| 400 | validation error | Поле role отсутствует или не входит в {admin, curator, student}. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Недостаточно прав — требуется роль администратора. |
| 404 | user not found | Пользователь с таким идентификатором не найден в школе. |
/api/v1/t/users/:id/permissionsПрава куратора
Возвращает набор детальных прав куратора. Доступно только роли admin. Целевой пользователь должен иметь роль curator.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор куратора. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/users/8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d/permissions \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "9c0d1e2f-3a4b-5c6d-7e8f-9a0b1c2d3e4f",
"user_id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"can_manage_courses": true,
"can_manage_students": true,
"can_manage_groups": true,
"can_view_analytics": false,
"can_manage_exams": false,
"created_at": "2026-05-28T12:30:00Z",
"updated_at": "2026-05-28T12:30:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid id format | Идентификатор не является корректным UUID. |
| 400 | user is not a curator | У целевого пользователя роль не curator. |
| 400 | permissions not found | Для куратора ещё не создан набор прав. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Недостаточно прав — требуется роль администратора. |
/api/v1/t/users/:id/permissionsОбновить права куратора
Перезаписывает набор детальных прав куратора. Доступно только роли admin. Целевой пользователь должен иметь роль curator. Эти флаги управляют гейтами manage_courses, manage_students, can_manage_groups, manage_exams, view_analytics.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор куратора. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
can_manage_coursesобяз. | boolean | Право управления курсами. |
can_manage_studentsобяз. | boolean | Право управления учениками. |
can_manage_groupsобяз. | boolean | Право управления группами. |
can_view_analyticsобяз. | boolean | Право просмотра аналитики. |
can_manage_examsобяз. | boolean | Право управления экзаменами. |
Пример запроса
curl -X PUT https://api.edumentor.kz/api/v1/t/users/8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d/permissions \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{
"can_manage_courses": true,
"can_manage_students": true,
"can_manage_groups": false,
"can_view_analytics": true,
"can_manage_exams": false
}'Пример ответа
{
"success": true,
"data": {
"id": "9c0d1e2f-3a4b-5c6d-7e8f-9a0b1c2d3e4f",
"user_id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"can_manage_courses": true,
"can_manage_students": true,
"can_manage_groups": false,
"can_view_analytics": true,
"can_manage_exams": false,
"created_at": "2026-05-28T12:30:00Z",
"updated_at": "2026-05-28T13:00:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid id format | Идентификатор не является корректным UUID. |
| 400 | user is not a curator | У целевого пользователя роль не curator. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 403 | forbidden | Недостаточно прав — требуется роль администратора. |
/api/v1/t/me/profileМой профиль
Возвращает собственную учётную запись вызывающего (с правами куратора, если применимо). Доступно любому авторизованному участнику школы — ученику, куратору или админу.
Пример запроса
curl https://api.edumentor.kz/api/v1/t/me/profile \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"tenant_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email": "student@acme.kz",
"first_name": "Дамир",
"last_name": "Сериков",
"avatar": null,
"role": "student",
"is_active": true,
"last_active_at": "2026-05-28T09:50:00Z",
"group_count": 2,
"groups": [
{ "group_id": "g1g1...", "name": "Поток 2026", "logo": "a1b2c3d4-0000-4000-8000-000000000000" },
{ "group_id": "g2g2...", "name": "Группа B", "logo": null }
],
"created_at": "2026-05-20T10:00:00Z",
"updated_at": "2026-05-28T09:50:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 401 | unauthorized | Токен отсутствует, истёк или недействителен либо школа не определена. |
| 404 | user not found | Учётная запись не найдена (удалена). |
/api/v1/t/me/profileОбновить мой профиль
Обновляет собственный профиль: имя, фамилию, аватар. Принимает два формата тела: application/json (как раньше — avatar это URL или UUID) и multipart/form-data с текстовыми полями first_name/last_name и необязательной частью-файлом avatar (загрузка изображения файлом). Поля email, role, is_active, tenant_id, password через этот эндпоинт не меняются — они молча игнорируются.
В режиме multipart часть-файл avatar загружается на сервере через media-сервис: только изображение (jpeg/png/webp/gif), реальный MIME проверяется сниффингом (а не заявленным заголовком), учитывается квота хранилища тенанта. Файл сохраняется как медиа-ассет, и в ответе avatar приходит готовым presigned-URL. Если части-файла нет, текстовое поле avatar трактуется как URL/UUID (контракт JSON-режима).
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
first_nameопц. | string | Имя (1–100 символов). JSON-поле или текстовая часть multipart. |
last_nameопц. | string | Фамилия (1–100 символов). JSON-поле или текстовая часть multipart. |
avatarопц. | string | file | В JSON — URL аватара или UUID медиа-ассета. В multipart — часть-файл с изображением (загружается на сервер) либо строковое значение URL/UUID, если файла нет. Для UUID/файла на чтение возвращается presigned-URL. |
Пример запроса
# JSON — имя/фамилия (+ avatar как URL/UUID)
curl -X PATCH https://api.edumentor.kz/api/v1/t/me/profile \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "first_name": "Дамир", "last_name": "Серик" }'
# multipart — загрузка аватара файлом
curl -X PATCH https://api.edumentor.kz/api/v1/t/me/profile \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-F "first_name=Дамир" \
-F "avatar=@/path/to/avatar.png"Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"tenant_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email": "student@acme.kz",
"first_name": "Дамир",
"last_name": "Серик",
"avatar": "https://storage.edumentor.kz/...?X-Amz-Signature=...",
"role": "student",
"is_active": true,
"created_at": "2026-05-20T10:00:00Z",
"updated_at": "2026-05-28T13:30:00Z"
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | validation error | Тело запроса не прошло валидацию (например, first_name длиннее 100 символов). |
| 400 | empty_file | Часть-файл avatar пуста (размер 0). |
| 400 | invalid_image_type | Загруженный файл — не изображение из разрешённого списка (jpeg/png/webp/gif). |
| 400 | avatar_too_large | Размер файла превышает лимит для изображений. |
| 400 | storage_quota_exceeded | Превышена квота хранилища тенанта. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен либо школа не определена. |
/api/v1/t/me/change-passwordСменить пароль
Меняет собственный пароль после проверки текущего. Доступно любому авторизованному участнику школы. Действующие JWT-токены НЕ инвалидируются (упрощение v1).
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
current_passwordобяз. | string | Текущий пароль (проверяется по bcrypt-хешу). |
new_passwordобяз. | string | Новый пароль (8–128 символов). |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/me/change-password \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "current_password": "old-secret", "new_password": "new-super-secret" }'Пример ответа
{
"success": true,
"data": { "changed": true }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_current_password | Текущий пароль не совпал с хранимым хешем. |
| 400 | user_account_inactive | Учётная запись деактивирована — смена пароля запрещена. |
| 400 | validation error | new_password короче 8 или длиннее 128 символов. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен либо школа не определена. |