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 администратора.
phoneобяз.stringТелефон для связи: + и 8–15 цифр, без пробелов. Сохраняется у администратора школы.
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",
    "phone": "+77011234567",
    "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 минут; одновременно активен только один код на пользователя — новый запрос инвалидирует предыдущий.

Письмо с кодом ставится в очередь отправки через Resend. При временной ошибке доставки выполняются повторные попытки. В отличие от обычной анти-перечислительной практики, эндпоинт намеренно отвечает 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Токен отсутствует, истёк или недействителен.