Public

Аутентификация

Вход, регистрация школы, сброс пароля по коду, автоопределение школ по email и профиль текущего пользователя. Эти эндпоинты не требуют авторизации (кроме /me).

POST/api/v1/auth/login

Вход в систему

Public Без авторизации

Проверяет email и пароль и создаёт управляемый сеанс устройства. Возвращает короткоживущий access JWT; браузеру refresh-токен передаётся только в защищённой HttpOnly cookie, а нативному приложению — в JSON. Поле subdomain указывает школу; пустое значение используется только для суперадмина платформы.

Тело запроса

ПараметрТипОписание
emailобяз.stringEmail пользователя.
passwordобяз.stringПароль.
subdomainопц.stringСубдомен школы (только латиница и цифры). Пусто — вход суперадмина (tenant_id IS NULL).Пример: acme
device_tokenопц.stringFCM-токен нативного мобильного устройства. При наличии токена успешный login гарантирует его сохранение.
installation_idопц.stringСтабильный локальный ID установки, не зависящий от FCM-токена. Обязателен для новых управляемых сеансов учеников после включения соответствующего rollout-режима.
device_platformопц.`web` | `ios` | `android`Платформа установки. Для запросов из браузера сервер принудительно использует web.
device_nameопц.stringПонятное пользователю название устройства, до 120 символов.
os_nameопц.stringНазвание операционной системы, до 64 символов.
os_versionопц.stringВерсия операционной системы, до 64 символов.
browser_nameопц.stringНазвание браузера, до 64 символов.
browser_versionопц.stringВерсия браузера, до 64 символов.
app_versionопц.stringВерсия нативного приложения, до 64 символов.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "teacher@acme.kz",
    "password": "super-secret",
    "subdomain": "acme",
    "installation_id": "web-installation-123",
    "device_platform": "web",
    "device_name": "Chrome · macOS",
    "os_name": "macOS",
    "browser_name": "Chrome"
  }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "access_expires_at": "2026-08-30T12:15:00Z",
    "refresh_expires_at": "2026-11-28T12:00:00Z",
    "session_id": "745810eb-...",
    "auth_device_id": "2b9d59e5-...",
    "user": {
      "id": "8f3b1c2a-...",
      "email": "teacher@acme.kz",
      "role": "admin",
      "first_name": "Айгерим",
      "last_name": "Нурлан"
    }
  }
}

Ошибки

КодerrorКогда
400invalid credentialsНеверный email или пароль (обобщённое сообщение — без раскрытия деталей).
403email_not_verifiedПароль верный, но школа ещё ожидает подтверждения email владельца.
403device_blockedАдминистратор школы запретил вход с этой установки.
409device_limit_reachedДостигнут лимит активных устройств. В data находятся безопасный список активных устройств и краткоживущий replacement_token, если самостоятельная замена разрешена.
409installation_id_requiredКлиент должен обновиться и передать стабильный installation_id.
423too many attempts, try again laterСработала защита от перебора — слишком много попыток входа.
503device_registration_failedУчетные данные верны, но переданный device_token не удалось сохранить; клиенту нужно повторить login.
POST/api/v1/auth/refresh

Продлить управляемый сеанс

Public Без авторизации

Проверяет и атомарно ротирует refresh-токен, затем выдаёт новый короткоживущий access JWT. Браузер передаёт сохранённые non-secret session_id и auth_device_id, по которым сервер выбирает точную HttpOnly cookie; JavaScript не читает и не сохраняет сам refresh-секрет. Нативный клиент передаёт refresh_token в JSON и обязан безопасно сохранить новый токен из ответа.

Тело запроса

ПараметрТипОписание
tenant_subdomainопц.stringСубдомен школы. Может также передаваться заголовком X-Tenant-Subdomain.
session_idопц.uuidОбязательный non-secret selector для браузера; значение из login/replace/refresh. Нативный клиент может не передавать.
auth_device_idопц.uuidОбязательный non-secret selector browser cookie; значение из login/replace/refresh. Нативный клиент может не передавать.
refresh_tokenопц.stringТолько для нативного клиента. Браузер использует HttpOnly cookie.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/auth/refresh   -H "Content-Type: application/json"   -H "X-Tenant-Subdomain: acme"   -b '<device-refresh-cookie>'   -d '{"session_id":"745810eb-...","auth_device_id":"2b9d59e5-..."}'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "access_expires_at": "2026-08-30T12:30:00Z",
    "refresh_expires_at": "2026-11-28T12:00:00Z",
    "session_id": "745810eb-...",
    "auth_device_id": "2b9d59e5-..."
  }
}

Ошибки

КодerrorКогда
400session_id_requiredВ browser-запросе нет корректного session_id selector; cookie и серверная сессия не изменяются.
400auth_device_id_requiredВ browser-запросе нет корректного auth_device_id selector; cookie и серверная сессия не изменяются.
400refresh_token_requiredНет cookie или нативного refresh_token.
401unauthorizedТочная cookie не найдена, selector не принадлежит её семье, либо сеанс недействителен, истёк или отозван. Ответ не раскрывает причину и ничего не изменяет при ошибке selector.
409stale_refresh_tokenПараллельный запрос уже ротировал токен; клиент должен использовать результат единственного refresh-запроса.
POST/api/v1/auth/logout

Завершить текущий сеанс

Public Без авторизации

Отзывает текущую серверную сессию и очищает только выбранную браузерную refresh-cookie. Браузер обязан передать non-secret session_id и auth_device_id; сервер проверяет их связь с точной cookie до отзыва. Повторный вызов с уже завершённым подтверждённым семейством безопасен.

Тело запроса

ПараметрТипОписание
tenant_subdomainопц.stringСубдомен школы или одноимённый заголовок.
session_idопц.uuidОбязательный selector для browser logout; нативный клиент может не передавать.
auth_device_idопц.uuidОбязательный selector для browser logout; нативный клиент может не передавать.
refresh_tokenопц.stringТолько для нативного клиента.

Ошибки

КодerrorКогда
400session_id_requiredНет корректного browser session selector; ни cookie, ни сессия не изменяются.
400auth_device_id_requiredНет корректного browser device selector; ни cookie, ни сессия не изменяются.
401unauthorizedCookie отсутствует, неоднозначна или не связана с selectors; причина намеренно не раскрывается.
POST/api/v1/auth/device-limit/replace

Заменить устройство при входе

Public Без авторизации

Продолжает отклонённый по лимиту вход: отзывает выбранное активное устройство и создаёт сеанс для новой установки. replacement_token короткоживущий, одноразовый и выдаётся только после успешной проверки пароля.

Тело запроса

ПараметрТипОписание
replacement_tokenобяз.stringТокен из ответа device_limit_reached.
revoke_device_idобяз.uuidОдно из активных устройств, возвращённых вместе с ошибкой лимита.
tenant_subdomainопц.stringСубдомен школы или одноимённый заголовок.

Ошибки

КодerrorКогда
403device_self_replacement_disabledШкола запретила самостоятельную замену.
409invalid_device_replacement_tokenТокен замены неверен, истёк или уже использован.
409device_limit_reachedСостояние изменилось конкурентно и лимит всё ещё превышен.
POST/api/v1/auth/register

Регистрация школы

Public Без авторизации

Создаёт новую школу (тенант) в статусе pending, её первого администратора и пробный биллинг-период. Письмо подтверждения ставится в outbox атомарно с регистрацией. В актуальном режиме управляемых сессий после подтверждения нужен обычный login с метаданными установки.

Тело запроса

ПараметрТипОписание
tenant_titleобяз.stringНазвание школы (2–255 символов).
tenant_slugобяз.stringУникальный slug школы (латиница/цифры, 2–100).Пример: acme
tenant_subdomainопц.stringСубдомен. Если не задан — берётся из slug.Пример: acme
emailобяз.stringEmail администратора.
passwordобяз.stringПароль (минимум 8 символов).
first_nameобяз.stringИмя администратора.
last_nameобяз.stringФамилия администратора.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_title": "Acme School",
    "tenant_slug": "acme",
    "email": "admin@acme.kz",
    "password": "super-secret",
    "first_name": "Айгерим",
    "last_name": "Нурлан"
  }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "verification_required": true,
    "email": "admin@acme.kz",
    "verification_expires_at": "2026-07-23T10:00:00Z"
  }
}

Ошибки

КодerrorКогда
400duplicate keyШкола с таким slug/субдоменом или пользователь с таким email уже существует.
POST/api/v1/auth/verify-email

Подтвердить email владельца

Public Без авторизации

Проверяет одноразовый токен из письма, отмечает email подтверждённым и переводит школу из pending в active. В актуальном режиме возвращает login_required=true: управляемый сеанс создаётся отдельным login. Поле token остаётся только для ограниченной legacy-совместимости.

Тело запроса

ПараметрТипОписание
tokenобяз.stringПодписанный одноразовый токен из verification-ссылки.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/auth/verify-email \
  -H "Content-Type: application/json" \
  -d '{ "token": "<token-from-email>" }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "login_required": true,
    "tenant_subdomain": "acme",
    "user": { "id": "8f3b1c2a-...", "email": "admin@acme.kz", "role": "admin" }
  }
}

Ошибки

КодerrorКогда
400invalid_or_expired_email_verificationТокен неверный, просрочен или уже использован.
POST/api/v1/auth/resend-verification

Повторить письмо подтверждения

Public Без авторизации

Инвалидирует старую ссылку и ставит новую в outbox для pending-школы. Ответ всегда нейтральный и не раскрывает существование школы или email; действует минутный cooldown.

Тело запроса

ПараметрТипОписание
emailобяз.stringEmail владельца школы.
subdomainобяз.stringИдентификатор школы.Пример: acme

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

200 OK · application/json
{
  "success": true,
  "data": { "message": "if the account is pending, a verification email has been queued" }
}
POST/api/v1/auth/forgot-password

Запросить код сброса пароля

Public Без авторизации

Создаёт 6-значный код подтверждения для сброса пароля. Поле subdomain указывает школу (для суперадмина — пусто). Код действует 15 минут; одновременно активен только один код на пользователя — новый запрос инвалидирует предыдущий.

⚠️ В текущей версии код не отправляется на email, а выводится в консоль сервера (интеграция с email-провайдером запланирована). В отличие от обычной анти-перечислительной практики, эндпоинт намеренно отвечает 404, если связки email + школа не существует.

Тело запроса

ПараметрТипОписание
emailобяз.stringEmail пользователя.
subdomainопц.stringСубдомен школы (латиница/цифры). Пусто — суперадмин.Пример: acme

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/auth/forgot-password \
  -H "Content-Type: application/json" \
  -d '{ "email": "teacher@acme.kz", "subdomain": "acme" }'

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

200 OK · application/json
{
  "success": true,
  "data": { "message": "a confirmation code has been sent" }
}

Ошибки

КодerrorКогда
400validation errorНекорректный email или невалидный субдомен.
404no account with this email and schoolНет аккаунта с такой связкой email + школа.
POST/api/v1/auth/reset-password

Сбросить пароль по коду

Public Без авторизации

Устанавливает новый пароль по действующему коду подтверждения. Код проверяется по связке email + школа. После 5 неверных попыток код блокируется (нужно запросить новый).

Тело запроса

ПараметрТипОписание
emailобяз.stringEmail пользователя.
subdomainопц.stringСубдомен школы (латиница/цифры). Пусто — суперадмин.Пример: acme
codeобяз.string6-значный код подтверждения (только цифры).Пример: 048213
new_passwordобяз.stringНовый пароль (минимум 8 символов).

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/auth/reset-password \
  -H "Content-Type: application/json" \
  -d '{
    "email": "teacher@acme.kz",
    "subdomain": "acme",
    "code": "048213",
    "new_password": "new-super-secret"
  }'

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

200 OK · application/json
{
  "success": true,
  "data": { "message": "password updated" }
}

Ошибки

КодerrorКогда
400invalid or expired confirmation codeКод неверный, просрочен (TTL 15 мин) или уже использован.
423too many attempts, request a new codeИсчерпан лимит из 5 попыток ввода кода — запросите новый.
POST/api/v1/auth/schools

Поиск школ по email

Public Без авторизации

Возвращает список активных школ, в которые может войти указанный email. Используется экраном входа для автоподстановки школы. Авторизация не требуется.

Тело запроса

ПараметрТипОписание
emailобяз.stringEmail для поиска.
scopeопц.stringФильтр по роли: admin — только кабинеты (админ/куратор), student — только школы, где email является учеником. Пусто — любая роль.Пример: admin

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/auth/schools \
  -H "Content-Type: application/json" \
  -d '{ "email": "teacher@acme.kz", "scope": "admin" }'

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

200 OK · application/json
{
  "success": true,
  "data": [
    { "subdomain": "acme", "title": "Acme School", "role": "admin" }
  ]
}
GET/api/v1/auth/me

Текущий пользователь

PublicTenantSuperadmin JWT

Возвращает профиль авторизованного пользователя. Для тенант-пользователей дополнительно отдаёт состояние биллинга школы (plan_state, expires_at, days_remaining) для баннера пробного периода. Для суперадмина блок школы отсутствует.

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "8f3b1c2a-...",
    "email": "admin@acme.kz",
    "role": "admin",
    "first_name": "Айгерим",
    "last_name": "Нурлан",
    "tenant": {
      "plan_state": "trial",
      "expires_at": "2026-06-12T00:00:00Z",
      "days_remaining": 14
    }
  }
}

Ошибки

КодerrorКогда
401unauthorizedТокен отсутствует, истёк или недействителен.