TenantAdminCuratorStudent

Push-уведомления

Self-service настройка согласия и FCM-устройств, полный каталог автоматических событий, scoped options/preview аудитории и immutable push-кампании. Management endpoints доступны администратору или куратору с `can_manage_notifications`.

GET/api/v1/t/notifications/me/preferences

Моя настройка push

Tenant JWT

Возвращает общий push-выключатель текущего пользователя. Если сохранённой настройки ещё нет, API возвращает effective default true; строка создаётся при первом PATCH. Фактическая доставка всё равно требует browser permission и активного устройства.

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "preference": {
      "push_enabled": true,
      "updated_at": "2026-07-21T05:00:00Z"
    }
  }
}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
PATCH/api/v1/t/notifications/me/preferences

Включить или выключить push

Tenant JWT

Атомарно меняет общий push-выключатель аккаунта. Выключение suppress-ит новые доставки, но не удаляет устройства; их можно включать, выключать и удалять отдельно.

Тело запроса

ПараметрТипОписание
push_enabledобяз.booleanРазрешена ли push-доставка для аккаунта.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/notifications/me/preferences \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{"push_enabled":false}'

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

200 OK · application/json
{"success":true,"data":{"preference":{"push_enabled":false,"updated_at":"2026-07-21T05:10:00Z"}}}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
400push_enabled is requiredПоле отсутствует или имеет не boolean-тип.
GET/api/v1/t/notifications/me

История моих уведомлений

Tenant JWT

Возвращает пагинированную историю успешно доставленных push текущего пользователя в текущей школе. Одна логическая запись возвращается один раз, даже если push был отправлен на несколько устройств. Отключённые, подавленные и окончательно не доставленные записи в историю не попадают.

Query-параметры

ПараметрТипОписание
pageопц.integerСтраница от 1; по умолчанию 1.
limitопц.integer1…100; по умолчанию 20.

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

cURL
curl "https://api.edumentor.kz/api/v1/t/notifications/me?page=1&limit=20" \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme"

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

200 OK · application/json
{
  "success": true,
  "data": {
    "notifications": [{
      "id": "6d7e8f90-...",
      "event_code": "assignment.accepted",
      "category": "assignment",
      "title": "Задание принято",
      "body": "Работа «Лабораторная №1» принята. Оценка: 95.00.",
      "deep_link": null,
      "data": {"event_code":"assignment.accepted"},
      "aggregate_type": "submission",
      "aggregate_id": "3c4d5e6f-...",
      "sent_at": "2026-07-21T06:30:00Z",
      "created_at": "2026-07-21T06:29:55Z"
    }],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
400invalid_paginationpage/limit не являются целыми числами или выходят за допустимый диапазон.
GET/api/v1/t/notifications/me/devices

Мои push-устройства

Tenant JWT

Возвращает только устройства текущего пользователя в текущей школе. Plaintext FCM token, hash и ciphertext никогда не сериализуются.

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

200 OK · application/json
{
  "success": true,
  "data": {
    "devices": [{
      "id": "2a2b3c4d-...",
      "installation_id": "d5e6f7a8-...",
      "provider": "fcm",
      "platform": "web",
      "enabled": true,
      "app_version": "2026.07.21",
      "last_seen_at": "2026-07-21T05:00:00Z",
      "created_at": "2026-07-20T11:00:00Z",
      "updated_at": "2026-07-21T05:00:00Z"
    }]
  }
}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
POST/api/v1/t/notifications/me/devices

Создать или обновить устройство

Tenant JWT

Идемпотентный upsert по (tenant, user, provider, installation_id). Повторный вызов обновляет ротированный token, platform и app_version и включает устройство. Если такой token уже принадлежал другой установке, старый binding инвалидируется безопасно.

Тело запроса

ПараметрТипОписание
installation_idобяз.stringСтабильный ID установки, 1…255 символов.
tokenобяз.stringWrite-only FCM registration token, 1…4096 символов.
platformобяз.stringweb, ios или android.
providerопц.stringТолько fcm; по умолчанию fcm.
app_versionопц.string|nullВерсия приложения, максимум 64 символа.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/notifications/me/devices \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{"installation_id":"d5e6f7a8-...","token":"<fcm-token>","provider":"fcm","platform":"web"}'

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

200 OK · application/json
{"success":true,"data":{"device":{"id":"2a2b3c4d-...","installation_id":"d5e6f7a8-...","provider":"fcm","platform":"web","enabled":true,"app_version":null,"last_seen_at":"2026-07-21T05:00:00Z","created_at":"2026-07-21T05:00:00Z","updated_at":"2026-07-21T05:00:00Z"}}}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
400invalid_request_bodyНевалидный installation_id, token, provider или platform.
503push_not_configuredНе задан PUSH_TOKEN_KEY_FILE с доступным 32-byte ключом.
PATCH/api/v1/t/notifications/me/devices/:id

Обновить устройство

Tenant JWT

Частично обновляет принадлежащее текущему пользователю устройство. installation_id и provider неизменяемы. Token остаётся write-only.

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

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

Тело запроса

ПараметрТипОписание
tokenопц.stringНовый FCM token при ротации.
platformопц.stringweb, ios или android.
enabledопц.booleanАктивно ли это устройство.
app_versionопц.string|nullВерсия приложения.

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

200 OK · application/json
{"success":true,"data":{"device":{"id":"2a2b3c4d-...","installation_id":"d5e6f7a8-...","provider":"fcm","platform":"web","enabled":false}}}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
404push_device_not_foundУстройство не найдено у текущего пользователя.
DELETE/api/v1/t/notifications/me/devices/:id

Удалить устройство

Tenant JWT

Мягко удаляет binding устройства текущего пользователя и необратимо очищает сохранённые ciphertext/hash токена. Клиент также должен вызвать Firebase deleteToken() для текущей установки.

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

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

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

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

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
404push_device_not_foundУстройство не найдено у текущего пользователя.
GET/api/v1/t/notifications/admin/events

Каталог и настройки событий

AdminCurator JWT право: manage_notifications

Возвращает все 63 hard-coded event definition вместе с effective tenant-настройкой. Для текущего каталога producer подключён у каждого события (implemented=true). Поле остаётся в контракте как защита для будущих событий, которые могут быть добавлены до подключения источника.

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

200 OK · application/json
{
  "success": true,
  "data": {
    "events": [{
      "code": "lesson.deadline_reminder",
      "category": "schedule",
      "name": "Напоминание о дедлайне",
      "description": "Ежедневное напоминание за N дней до закрытия урока",
      "default_enabled": true,
      "default_config": {"days_before":3,"repeat_daily":true,"send_at_local":"09:00"},
      "implemented": true,
      "source": "deadline_scan",
      "recipients": "students",
      "variables": ["lesson_title","deadline","days_left"],
      "enabled": true,
      "config": {"days_before":3,"repeat_daily":true,"send_at_local":"09:00"},
      "title_template": "Скоро дедлайн урока",
      "body_template": "Завершите «{{lesson_title}}» до {{deadline}}.",
      "overridden": false
    }]
  }
}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
PATCH/api/v1/t/notifications/admin/events/:code

Переопределить событие

AdminCurator JWT право: manage_notifications

Upsert effective-настройки известного и реализованного event code. Код события создать через API нельзя. Шаблоны trim-ятся и валидируются в UTF-8 bytes.

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

ПараметрТипОписание
codeобяз.stringКод из server catalog.

Тело запроса

ПараметрТипОписание
enabledопц.booleanВключено ли автоматическое событие.
configопц.objectConfig известного события. Для lesson.deadline_reminder и assignment.deadline_reminder: days_before — целое 1…90, repeat_daily — boolean, send_at_local — HH:MM. Для progress.stalled: inactive_days — целое 1…365, send_at_local — HH:MM. Для billing.expiry_reminder: days_before — непустой массив максимум из 30 целых значений 1…365, send_at_local — HH:MM.
title_templateопц.stringНепустой шаблон, максимум 160 UTF-8 bytes.
body_templateопц.stringНепустой шаблон, максимум 2000 UTF-8 bytes.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/notifications/admin/events/lesson.deadline_reminder \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"config":{"days_before":3,"repeat_daily":true,"send_at_local":"09:00"}}'

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
400notification_validation_failedНевалидный config или превышен byte-limit шаблона.
404notification_event_not_foundКод отсутствует в catalog.
409notification_event_not_implementedProducer события ещё не подключён.
GET/api/v1/t/notifications/admin/audience/options

Варианты для конструктора аудитории

AdminCurator JWT право: manage_notifications

Возвращает минимальные id/label/subtitle без зависимости от других curator permissions. Для куратора students ограничены его учениками, groups — назначенными группами, а остальные объекты — связанными с его student scope.

Query-параметры

ПараметрТипОписание
kindобяз.stringstudent, group, course, module, lesson, quiz, assignment, exam или path.
qопц.stringПоиск по label/subtitle.
limitопц.integer1…50, по умолчанию 20.

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

200 OK · application/json
{"success":true,"data":{"kind":"course","options":[{"id":"1a2b3c4d-...","label":"Математика ҰБТ","subtitle":"published"}],"total":1,"limit":20}}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
400invalid_notification_audienceНеизвестный kind или невалидный limit.
POST/api/v1/t/notifications/admin/audience/preview

Предпросмотр аудитории

AdminCurator JWT право: manage_notifications

Считает активных учеников, соответствующих filter, и отдаёт небольшую PII-выборку. Ничего не сохраняет и не отправляет. Результат для куратора всегда пересекается с его student scope.

Пустой rules означает всех активных учеников tenant. Relation-поля поддерживают in/not_in; *_score_pct — values UUID[] + gte/lte value 0…100; points_total/rank — gte/lte; last_active_at — before/after RFC3339; push_enabled и has_device — eq со строкой true/false.

Тело запроса

ПараметрТипОписание
filterобяз.object{match:'all'|'any', rules:[{field,operator,values?|value?}]}.
sample_limitопц.integerРазмер sample 1…100.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/notifications/admin/audience/preview \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{"filter":{"match":"all","rules":[{"field":"course_enrollment","operator":"in","values":["1a2b3c4d-..."]},{"field":"has_device","operator":"eq","value":"true"}]},"sample_limit":20}'

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

200 OK · application/json
{"success":true,"data":{"audience":{"count":42,"sample":[{"id":"9a8b7c6d-...","email":"student@example.kz","first_name":"Алия","last_name":"С."}],"sample_limit":20,"capped":true}}}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
400invalid_notification_audienceНеизвестное поле/оператор, пустые values, invalid UUID/RFC3339/boolean.
POST/api/v1/t/notifications/admin/campaigns

Создать push-кампанию

AdminCurator JWT право: manage_notifications

Повторно вычисляет аудиторию, сохраняет immutable snapshot получателей и создаёт scheduled campaign. scheduled_at null/omitted означает NOW. Preview не является авторитетным: фактический размер находится в campaign.audience_count.

Тело запроса

ПараметрТипОписание
titleобяз.stringНепустой, максимум 160 UTF-8 bytes.
bodyобяз.stringНепустой, максимум 2000 UTF-8 bytes.
deep_linkопц.string|nullТолько безопасный same-origin relative path, максимум 1000 символов.
filterобяз.objectAudience filter как в preview.
scheduled_atопц.RFC3339|nullБудущее время; null = отправить сейчас.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/notifications/admin/campaigns \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{"title":"Занятие перенесено","body":"Откройте новое расписание.","deep_link":"/courses","filter":{"match":"all","rules":[{"field":"group_id","operator":"in","values":["1a2b3c4d-..."]}]},"scheduled_at":null}'

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

200 OK · application/json
{
  "success": true,
  "data": {"campaign": {
    "id":"4a5b6c7d-...","status":"scheduled","title":"Занятие перенесено",
    "audience_filter":{"match":"all","rules":[{"field":"group_id","operator":"in","values":["1a2b3c4d-..."]}]},
    "audience_count":42,"outbox_created_count":0,"sent_count":0,"failed_count":0,
    "scheduled_at":"2026-07-21T05:20:00Z","started_at":null,"completed_at":null,"cancelled_at":null
  }}
}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
400notification_validation_failedПустой/слишком большой текст, unsafe deep_link или past scheduled_at.
400invalid_notification_audienceНевалидный filter.
GET/api/v1/t/notifications/admin/campaigns

История кампаний

AdminCurator JWT право: manage_notifications

Пагинированная история tenant. Куратор видит только созданные им кампании; администратор — все кампании школы.

Query-параметры

ПараметрТипОписание
pageопц.integerСтраница от 1.
limitопц.integerРазмер страницы.

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

200 OK · application/json
{"success":true,"data":{"campaigns":[{"id":"4a5b6c7d-...","status":"completed","title":"Занятие перенесено","audience_count":42,"outbox_created_count":42,"sent_count":40,"failed_count":2}],"total":1,"page":1,"limit":20}}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
GET/api/v1/t/notifications/admin/campaigns/:id

Детали кампании

AdminCurator JWT право: manage_notifications

Возвращает campaign со snapshot filter и delivery counters. Куратор может читать только созданную им кампанию.

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

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

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
404notification_campaign_not_foundКампания не найдена в доступном scope.
POST/api/v1/t/notifications/admin/campaigns/:id/cancel

Отменить кампанию

AdminCurator JWT право: manage_notifications

Переводит scheduled/processing кампанию в cancelled и suppress-ит ещё не отправленные записи. Уже доставленные push не отзываются. Куратор может отменить только свою кампанию.

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

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

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

200 OK · application/json
{"success":true,"data":{"campaign":{"id":"4a5b6c7d-...","status":"cancelled","cancelled_at":"2026-07-21T05:15:00Z"}}}

Ошибки

КодerrorКогда
401unauthorizedJWT отсутствует, истёк или недействителен.
403forbiddenНет tenant-доступа или права can_manage_notifications.
404notification_campaign_not_foundКампания не найдена в доступном scope.
409notification_campaign_immutableКампания уже completed/cancelled/failed и не может быть отменена.