TenantAdminCurator

Курсы и контент

Курсы, модули, уроки и контент-блоки — иерархия учебного материала.

POST/api/v1/t/courses

Создать курс

AdminCurator JWT право: manage_courses

Создаёт новый курс в статусе draft. Slug должен быть уникальным в пределах школы. Описание санируется на сервере.

Тело запроса

ПараметрТипОписание
titleобяз.stringНазвание курса (2–255 символов).
slugобяз.stringУникальный slug курса (2–120 символов).Пример: intro-to-go
descriptionопц.stringОписание курса (до 10000 символов, поддерживает rich text).
is_privateопц.booleanПриватный курс: виден только назначенным ученикам. По умолчанию false.
self_enrollment_enabledопц.booleanРазрешить свободную запись. По умолчанию false.
cover_presetопц.stringID готового градиентного фона обложки.
cover_colorопц.stringСвой цвет фона в формате #RRGGBB. Имеет приоритет над пресетом.
cover_iconопц.stringID иконки из каталога обложек.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/courses \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Введение в Go",
    "slug": "intro-to-go",
    "description": "Базовый курс по языку Go."
  }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "c1a2b3c4-...",
    "tenant_id": "8f3b1c2a-...",
    "title": "Введение в Go",
    "slug": "intro-to-go",
    "description": "Базовый курс по языку Go.",
    "status": "draft",
    "sequential_required": false,
    "position": 0,
    "created_at": "2026-05-28T10:00:00Z",
    "updated_at": "2026-05-28T10:00:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию (например, слишком короткий title или slug).
400duplicate_slugКурс с таким slug уже существует в этой школе.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
GET/api/v1/t/courses

Список курсов

Tenant JWT

Постраничный список курсов школы с агрегатами (число учеников, процент завершения, total_blocks — число блоков контента) и счётчиками по статусам для фильтр-пилюль. Каждый курс содержит cover_image_url — готовую ссылку на обложку (свежий presigned GET для cover_image_id; пусто, если обложки нет). Доступен любому авторизованному пользователю; ученики видят только опубликованные курсы (для них counts.draft всегда 0).

Фильтр status принимает только published или draft; любое иное значение тихо игнорируется (как отсутствие фильтра).

Query-параметры

ПараметрТипОписание
pageопц.integerНомер страницы (>= 1). По умолчанию 1.Пример: 1
limitопц.integerРазмер страницы (1–100). По умолчанию 20.Пример: 20
qопц.stringПоиск по названию/описанию курса.
statusопц.stringФильтр по статусу: published или draft.Пример: published
enrolledопц.booleanФильтр для студента: true — только курсы, на которые он записан; false — только не записанные. Игнорируется для админа/куратора.

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "courses": [
      {
        "id": "c1a2b3c4-...",
        "title": "Введение в Go",
        "slug": "intro-to-go",
        "status": "published",
        "sequential_required": false,
        "position": 0,
        "cover_image_id": "a9b8c7d6-...",
        "cover_image_url": "https://storage.edumentor.kz/...?X-Amz-Signature=...",
        "cover_preset": "ocean",
        "cover_icon": "graduation-cap",
        "auth_has_access": true,
        "students_count": 42,
        "completion_rate": 67,
        "total_blocks": 12,
        "created_at": "2026-05-28T10:00:00Z",
        "updated_at": "2026-05-28T10:00:00Z"
      }
    ],
    "counts": { "all": 12, "published": 8, "draft": 4 },
    "total": 8,
    "page": 1,
    "limit": 20
  }
}

Ошибки

КодerrorКогда
400invalid_paramspage < 1, limit < 1 или limit > 100.
401unauthorizedТокен отсутствует, истёк или недействителен.
GET/api/v1/t/courses/:id

Получить курс

Tenant JWT

Возвращает один курс по идентификатору вместе с производными агрегатами: cover_image_url — готовая ссылка на обложку (свежий presigned GET для cover_image_id), total_blocks — число блоков контента, total_lessons — число уроков, total_students — число записанных студентов. Для зачисленного студента дополнительно приходят progress (его прогресс по курсу) и resume (урок, на котором он остановился). Эти два поля отсутствуют для админа/куратора и для незачисленного студента. Ученики получают только опубликованные курсы; для черновика им вернётся 404.

auth_has_access (boolean) — записан ли текущий студент на курс; для админа/куратора всегда false. progress.total_blocks — персональный снимок обязательных видимых блоков: необязательные узлы и скрытый для групп контент исключаются. Он может отличаться от верхнеуровневого total_blocks, который считает весь авторский контент. resume.lesson_index — сквозной 0-based индекс урока по доступной ученику структуре. resume отсутствует, если студент ещё не открывал ни одного блока. Если на курс задано расписание, для студента приходят поля статуса окна: locked (boolean), locked_reason (course_not_started|course_ended), available_from/available_until (ISO8601|null) — см. раздел «Расписание курса».

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

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

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "c1a2b3c4-...",
    "tenant_id": "8f3b1c2a-...",
    "title": "Введение в Go",
    "slug": "intro-to-go",
    "description": "Базовый курс по языку Go.",
    "cover_image_id": "a9b8c7d6-...",
    "cover_image_url": "https://storage.edumentor.kz/...?X-Amz-Signature=...",
    "cover_preset": "ocean",
    "cover_icon": "graduation-cap",
    "status": "published",
    "sequential_required": false,
    "position": 0,
    "auth_has_access": true,
    "total_blocks": 12,
    "total_lessons": 4,
    "total_students": 34,
    "progress": {
      "blocks_completed": 5,
      "total_blocks": 12,
      "pct": 41,
      "completed_at": null
    },
    "resume": {
      "lesson_index": 1,
      "module_id": "m1a2b3c4-...",
      "lesson_id": "l1a2b3c4-..."
    },
    "created_at": "2026-05-28T10:00:00Z",
    "updated_at": "2026-05-28T10:00:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
404course_not_foundКурс не найден (или скрыт от ученика как черновик).
PATCH/api/v1/t/courses/:id

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

AdminCurator JWT право: manage_courses

Частичное обновление курса. Все поля опциональны — присылайте только изменяемые. cover_preset, cover_color и cover_icon настраивают сгенерированную обложку; загруженное изображение, если оно привязано, отображается вместо неё. Флаг sequential_required включает/выключает последовательное прохождение блоков учениками (админ/куратор обходят гейтинг).

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

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

Тело запроса

ПараметрТипОписание
titleопц.stringНовое название (2–255 символов).
slugопц.stringНовый slug (2–120 символов, уникален в школе).
descriptionопц.stringНовое описание (до 10000 символов).
cover_presetопц.stringID готового фона; пустая строка отключает пресет.
cover_colorопц.stringСвой цвет #RRGGBB; пустая строка возвращает пресет.
cover_iconопц.stringID иконки из каталога обложек.
is_privateопц.booleanПриватный курс: виден только назначенным ученикам. По умолчанию false.
self_enrollment_enabledопц.booleanРазрешить свободную запись. По умолчанию false. Закрытие не снимает существующий доступ.
sequential_requiredопц.booleanВключить последовательное прохождение блоков учениками.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/courses/c1a2b3c4-... \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Введение в Go (обновлено)", "sequential_required": true }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "c1a2b3c4-...",
    "title": "Введение в Go (обновлено)",
    "slug": "intro-to-go",
    "status": "published",
    "sequential_required": true,
    "updated_at": "2026-05-28T11:00:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
400invalid_cover_styleНеизвестный пресет/иконка или неверный формат цвета.
400duplicate_slugКурс с таким slug уже существует в этой школе.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404course_not_foundКурс не найден.
DELETE/api/v1/t/courses/:id

Удалить курс

AdminCurator JWT право: manage_courses

Мягкое удаление курса (soft delete). Если курс используется в активной траектории или к нему привязаны зачисленные группы, удаление блокируется — сначала снимите эти связи. Успех — 204 No Content без тела.

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

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

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

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

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

200 OK · application/json
204 No Content

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
409course_in_active_pathКурс используется одной или несколькими активными траекториями обучения — сначала отвяжите его.
409course_has_group_enrollmentsНа курс ещё зачислена хотя бы одна группа.
POST/api/v1/t/courses/:id/publish

Опубликовать курс

AdminCurator JWT право: manage_courses

Переводит курс в статус published. Идемпотентно. Возвращает новый статус.

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

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

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

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

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

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

Ошибки

КодerrorКогда
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404course_not_foundКурс не найден.
POST/api/v1/t/courses/:id/unpublish

Снять курс с публикации

AdminCurator JWT право: manage_courses

Переводит курс обратно в статус draft. Идемпотентно. Возвращает новый статус.

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

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

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

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

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

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

Ошибки

КодerrorКогда
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404course_not_foundКурс не найден.
PATCH/api/v1/t/courses/:id/cover-image

Установить обложку курса

AdminCurator JWT право: manage_courses

Привязывает к курсу медиа-актив в качестве обложки. cover_image_id должен ссылаться на существующий актив той же школы.

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

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

Тело запроса

ПараметрТипОписание
cover_image_idобяз.uuidИдентификатор медиа-актива для обложки.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/courses/c1a2b3c4-.../cover-image \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "cover_image_id": "a9b8c7d6-..." }'

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

200 OK · application/json
{
  "success": true,
  "data": { "cover_image_id": "a9b8c7d6-..." }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
400invalid_cover_imageМедиа-актив не найден, не является изображением или принадлежит другой школе.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404course_not_foundКурс не найден.
DELETE/api/v1/t/courses/:id/cover-image

Удалить изображение обложки курса

AdminCurator JWT право: manage_courses

Отвязывает загруженное изображение от курса, не удаляя сам медиа-актив. После этого web-клиенты показывают настроенный цветной фон и иконку.

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

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

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

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

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

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

Ошибки

КодerrorКогда
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404course_not_foundКурс не найден.
POST/api/v1/t/courses/:id/duplicate

Дублировать курс

AdminCurator JWT право: manage_courses

Создаёт глубокую копию курса вместе с не-удалёнными модулями, уроками, контент-блоками и настройками видимости по группам. Тело запроса пустое. Новый курс всегда в статусе draft; название = исходное + (копия), slug = исходный + -copy (с суффиксами -copy-2, -copy-3 при коллизии). Медиа, тесты и задания остаются общими ссылками внутри школы, поэтому история попыток и отправок у них тоже общая. Расписание и прогресс по курсу/урокам не копируются.

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

ПараметрТипОписание
idобяз.uuidИдентификатор исходного курса.

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "d2e3f4a5-...",
    "title": "Введение в Go (копия)",
    "slug": "intro-to-go-copy",
    "status": "draft",
    "sequential_required": false,
    "created_at": "2026-05-28T12:00:00Z",
    "updated_at": "2026-05-28T12:00:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404course_not_foundИсходный курс не найден или принадлежит другой школе.
POST/api/v1/t/courses/bulk-publish

Массовая публикация курсов

AdminCurator JWT право: manage_courses

Публикует список курсов одним запросом. Идемпотентно и ограничено школой: уже опубликованные, чужие (другой школы) и удалённые ID тихо пропускаются (счётчик skipped).

Тело запроса

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

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/courses/bulk-publish \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "ids": ["c1a2b3c4-...", "d2e3f4a5-..."] }'

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

200 OK · application/json
{
  "success": true,
  "data": { "affected": 2, "skipped": 0 }
}

Ошибки

КодerrorКогда
400invalid_paramsПустой массив, более 100 ID или некорректный UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
POST/api/v1/t/courses/bulk-unpublish

Массовое снятие курсов с публикации

AdminCurator JWT право: manage_courses

Симметрично массовой публикации: переводит список курсов из published в draft. Идемпотентно и ограничено школой; неподходящие ID пропускаются.

Тело запроса

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

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/courses/bulk-unpublish \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "ids": ["c1a2b3c4-...", "d2e3f4a5-..."] }'

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

200 OK · application/json
{
  "success": true,
  "data": { "affected": 2, "skipped": 0 }
}

Ошибки

КодerrorКогда
400invalid_paramsПустой массив, более 100 ID или некорректный UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
POST/api/v1/t/courses/:id/modules

Создать модуль

AdminCurator JWT право: manage_courses

Создаёт модуль внутри курса. Позиция назначается автоматически (в конец списка). Описание санируется на сервере. is_optional=true исключает весь модуль из обязательного прогресса и последовательного гейтинга.

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

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

Тело запроса

ПараметрТипОписание
titleобяз.stringНазвание модуля (1–255 символов).
descriptionопц.stringОписание модуля (до 10000 символов).
is_optionalопц.booleanНе требовать прохождение модуля и его дочернего контента. По умолчанию false.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/courses/c1a2b3c4-.../modules \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Модуль 1. Основы", "description": "Синтаксис и типы." }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "m1a2b3c4-...",
    "course_id": "c1a2b3c4-...",
    "tenant_id": "8f3b1c2a-...",
    "title": "Модуль 1. Основы",
    "description": "Синтаксис и типы.",
    "is_optional": false,
    "position": 0,
    "created_at": "2026-05-28T10:05:00Z",
    "updated_at": "2026-05-28T10:05:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
GET/api/v1/t/courses/:id/modules

Список модулей курса

Tenant JWT

Возвращает модули курса, упорядоченные по полю position, в конверте { modules, total_lessons }. Каждый модуль дополнен полем lessons_count (число не-удалённых уроков) и массивом lessons — упорядоченными уроками модуля. Каждый урок содержит passed (пройден или является необязательным для студента) и облегчённый список blocks (без presigned-ссылок и встроенных quiz/assignment). total_lessons — сумма уроков по всему курсу. Доступно любому авторизованному пользователю школы; студент видит только доступные ему по группам уроки.

Для студента passed=true, когда завершены все обязательные блоки урока; пустой или эффективно необязательный урок считается проходным. Необязательность наследуется сверху: module.is_optional || lesson.is_optional || block.is_optional. Необязательный контент остаётся доступным, не входит в прогресс и не блокирует следующий обязательный блок. Скрытые для групп модули/уроки также не входят в доступную цепочку. Расписание применяется независимо и всё ещё может закрыть необязательный материал. Блоки типа quiz_ref дополнительно возвращают question_count (integer).

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

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

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "modules": [
      {
        "id": "m1a2b3c4-...",
        "course_id": "c1a2b3c4-...",
        "title": "Модуль 1. Основы",
        "description": "Синтаксис и типы.",
        "is_optional": false,
        "position": 0,
        "lessons_count": 4,
        "lessons": [
          {
            "id": "l1a2b3c4-...",
            "module_id": "m1a2b3c4-...",
            "title": "Урок 1. Установка",
            "is_optional": false,
            "screenshot_protection_enabled": true,
            "position": 0,
            "passed": true,
            "blocks": [
              {
                "id": "b1a2b3c4-...",
                "type": "video",
                "position": 0,
                "is_optional": false,
                "payload": { "title": "Лекция 1", "duration_seconds": 615 }
              },
              {
                "id": "b2a2b3c4-...",
                "type": "quiz_ref",
                "position": 1,
                "payload": {},
                "question_count": 5
              }
            ]
          }
        ],
        "created_at": "2026-05-28T10:05:00Z",
        "updated_at": "2026-05-28T10:05:00Z"
      }
    ],
    "total_lessons": 4
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
GET/api/v1/t/modules/:id

Получить модуль

Tenant JWT

Возвращает один модуль по идентификатору.

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

ПараметрТипОписание
idобяз.uuidИдентификатор модуля.

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "m1a2b3c4-...",
    "course_id": "c1a2b3c4-...",
    "title": "Модуль 1. Основы",
    "description": "Синтаксис и типы.",
    "position": 0,
    "created_at": "2026-05-28T10:05:00Z",
    "updated_at": "2026-05-28T10:05:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
404module_not_foundМодуль не найден.
PATCH/api/v1/t/modules/:id

Обновить модуль

AdminCurator JWT право: manage_courses

Частичное обновление модуля. Все поля опциональны — присылайте только изменяемые.

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

ПараметрТипОписание
idобяз.uuidИдентификатор модуля.

Тело запроса

ПараметрТипОписание
titleопц.stringНовое название (1–255 символов).
descriptionопц.stringНовое описание (до 10000 символов).
is_optionalопц.booleantrue — модуль и всё его поддерево можно пропустить без влияния на прогресс и разблокировку.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/modules/m1a2b3c4-... \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Модуль 1. Введение" }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "m1a2b3c4-...",
    "course_id": "c1a2b3c4-...",
    "title": "Модуль 1. Введение",
    "position": 0,
    "updated_at": "2026-05-28T11:10:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404module_not_foundМодуль не найден.
DELETE/api/v1/t/modules/:id

Удалить модуль

AdminCurator JWT право: manage_courses

Мягкое удаление модуля (soft delete). Успех — 204 No Content без тела.

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

ПараметрТипОписание
idобяз.uuidИдентификатор модуля.

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

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

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

200 OK · application/json
204 No Content

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
POST/api/v1/t/modules/:id/duplicate

Копировать модуль

AdminCurator JWT право: manage_courses

Создаёт глубокую копию модуля в конце выбранного курса. Копируются только активные уроки и контент-блоки, обложки, настройки видимости и флаги необязательности; ссылки на медиа, тесты и задания остаются общими внутри школы, поэтому история попыток и отправок у них тоже общая. Название копии получает суффикс (копия). Расписание и прогресс по модулю/урокам не копируются. Для копирования в тот же курс передайте ID текущего курса.

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

ПараметрТипОписание
idобяз.uuidИдентификатор исходного модуля.

Тело запроса

ПараметрТипОписание
target_course_idобяз.uuidИдентификатор курса назначения в той же школе.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/modules/m1a2b3c4-.../duplicate \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "target_course_id": "c9d8e7f6-..." }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "m9d8e7f6-...",
    "course_id": "c9d8e7f6-...",
    "title": "Модуль 1. Введение (копия)",
    "position": 4,
    "created_at": "2026-08-05T10:00:00Z",
    "updated_at": "2026-08-05T10:00:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
400invalid_request_bodytarget_course_id отсутствует или не является UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404module_not_foundИсходный модуль не найден или принадлежит другой школе.
404course_not_foundКурс назначения не найден или принадлежит другой школе.
PATCH/api/v1/t/courses/:id/reorder-modules

Переупорядочить модули

AdminCurator JWT право: manage_courses

Задаёт новый порядок модулей внутри курса (drag-and-drop). Массив ordered_ids должен содержать полный набор идентификаторов модулей курса без дубликатов и пропусков.

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

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

Тело запроса

ПараметрТипОписание
ordered_idsобяз.string[]Полный список UUID модулей в желаемом порядке (минимум 1).

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/courses/c1a2b3c4-.../reorder-modules \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "ordered_ids": ["m2-...", "m1-...", "m3-..."] }'

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

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

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию (пустой массив или некорректный UUID).
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
409reorder_duplicateВ ordered_ids есть повторяющиеся идентификаторы.
409reorder_incompleteСписок не совпадает с полным набором модулей курса (пропуски или лишние ID).
PATCH/api/v1/t/modules/:id/cover-image

Установить обложку модуля

AdminCurator JWT право: manage_courses

Привязывает к модулю медиа-актив в качестве обложки. cover_image_id должен ссылаться на существующий актив той же школы.

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

ПараметрТипОписание
idобяз.uuidИдентификатор модуля.

Тело запроса

ПараметрТипОписание
cover_image_idобяз.uuidИдентификатор медиа-актива для обложки.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/modules/m1a2b3c4-.../cover-image \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "cover_image_id": "a9b8c7d6-..." }'

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

200 OK · application/json
{
  "success": true,
  "data": { "cover_image_id": "a9b8c7d6-..." }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
400invalid_cover_imageМедиа-актив не найден или принадлежит другой школе.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404module_not_foundМодуль не найден.
POST/api/v1/t/modules/:id/lessons

Создать урок

AdminCurator JWT право: manage_courses

Создаёт урок внутри модуля. Позиция назначается автоматически (в конец списка). Описание (rich text) санируется на сервере. is_optional=true исключает урок и его блоки из обязательного прогресса и последовательного гейтинга. Поле screenshot_protection_enabled сообщает мобильному приложению, нужно ли блокировать скриншоты при открытом уроке.

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

ПараметрТипОписание
idобяз.uuidИдентификатор модуля.

Тело запроса

ПараметрТипОписание
titleобяз.stringНазвание урока (1–255 символов).
descriptionопц.stringОписание урока (rich text, до 50000 символов).
is_optionalопц.booleanНе требовать прохождение урока и его блоков. По умолчанию false.
screenshot_protection_enabledопц.booleantrue — мобильное приложение должно блокировать скриншоты. По умолчанию false.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/modules/m1a2b3c4-.../lessons \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Урок 1. Hello, World", "description": "<p>Первая программа.</p>", "screenshot_protection_enabled": true }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "l1a2b3c4-...",
    "module_id": "m1a2b3c4-...",
    "tenant_id": "8f3b1c2a-...",
    "title": "Урок 1. Hello, World",
    "description": "<p>Первая программа.</p>",
    "is_optional": false,
    "screenshot_protection_enabled": true,
    "position": 0,
    "created_at": "2026-05-28T10:10:00Z",
    "updated_at": "2026-05-28T10:10:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
GET/api/v1/t/modules/:id/lessons

Список уроков модуля

Tenant JWT

Возвращает уроки модуля, упорядоченные по полю position. Доступно любому авторизованному пользователю школы. Поле data — массив уроков. Каждый урок содержит screenshot_protection_enabled для мобильного клиента.

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

ПараметрТипОписание
idобяз.uuidИдентификатор модуля.

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

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

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

200 OK · application/json
{
  "success": true,
  "data": [
    {
      "id": "l1a2b3c4-...",
      "module_id": "m1a2b3c4-...",
      "title": "Урок 1. Hello, World",
      "description": "<p>Первая программа.</p>",
      "screenshot_protection_enabled": true,
      "position": 0,
      "created_at": "2026-05-28T10:10:00Z",
      "updated_at": "2026-05-28T10:10:00Z"
    }
  ]
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
GET/api/v1/t/lessons/:id

Получить урок

Tenant JWT

Возвращает один урок по идентификатору, включая screenshot_protection_enabled для мобильного клиента.

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

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

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "l1a2b3c4-...",
    "module_id": "m1a2b3c4-...",
    "title": "Урок 1. Hello, World",
    "description": "<p>Первая программа.</p>",
    "screenshot_protection_enabled": true,
    "position": 0,
    "created_at": "2026-05-28T10:10:00Z",
    "updated_at": "2026-05-28T10:10:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
404lesson_not_foundУрок не найден.
PATCH/api/v1/t/lessons/:id

Обновить урок

AdminCurator JWT право: manage_courses

Частичное обновление урока. Все поля опциональны. Описание (rich text) санируется на сервере. Защиту от скриншотов можно включать и отключать независимо для каждого урока.

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

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

Тело запроса

ПараметрТипОписание
titleопц.stringНовое название (1–255 символов).
descriptionопц.stringНовое описание (rich text, до 50000 символов).
is_optionalопц.booleantrue — урок и его блоки можно пропустить.
screenshot_protection_enabledопц.booleantrue — блокировать скриншоты в мобильном приложении; false — разрешить.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/lessons/l1a2b3c4-... \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Урок 1. Первая программа", "screenshot_protection_enabled": false }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "l1a2b3c4-...",
    "module_id": "m1a2b3c4-...",
    "title": "Урок 1. Первая программа",
    "screenshot_protection_enabled": false,
    "position": 0,
    "updated_at": "2026-05-28T11:15:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404lesson_not_foundУрок не найден.
DELETE/api/v1/t/lessons/:id

Удалить урок

AdminCurator JWT право: manage_courses

Мягкое удаление урока (soft delete). Успех — 204 No Content без тела.

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

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

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

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

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

200 OK · application/json
204 No Content

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
POST/api/v1/t/lessons/:id/duplicate

Копировать урок

AdminCurator JWT право: manage_courses

Создаёт глубокую копию урока вместе с активными контент-блоками в конце выбранного модуля. Обложка, защита от скриншотов и настройки видимости сохраняются; ссылки на медиа, тесты и задания остаются общими внутри школы, поэтому история попыток и отправок у них тоже общая. Название копии получает суффикс (копия). Расписание и прогресс по уроку не копируются. Чтобы добавить копию в конец того же модуля, передайте ID текущего модуля.

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

ПараметрТипОписание
idобяз.uuidИдентификатор исходного урока.

Тело запроса

ПараметрТипОписание
target_module_idобяз.uuidИдентификатор модуля назначения в той же школе.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/lessons/l1a2b3c4-.../duplicate \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "target_module_id": "m9d8e7f6-..." }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "l9d8e7f6-...",
    "module_id": "m9d8e7f6-...",
    "title": "Урок 1. Hello, World (копия)",
    "screenshot_protection_enabled": true,
    "position": 6,
    "created_at": "2026-08-05T10:00:00Z",
    "updated_at": "2026-08-05T10:00:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
400invalid_request_bodytarget_module_id отсутствует или не является UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404lesson_not_foundИсходный урок не найден или принадлежит другой школе.
404module_not_foundМодуль назначения не найден или принадлежит другой школе.
PATCH/api/v1/t/modules/:id/reorder-lessons

Переупорядочить уроки

AdminCurator JWT право: manage_courses

Задаёт новый порядок уроков внутри модуля (drag-and-drop). Массив ordered_ids должен содержать полный набор идентификаторов уроков модуля без дубликатов и пропусков.

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

ПараметрТипОписание
idобяз.uuidИдентификатор модуля.

Тело запроса

ПараметрТипОписание
ordered_idsобяз.string[]Полный список UUID уроков в желаемом порядке (минимум 1).

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/modules/m1a2b3c4-.../reorder-lessons \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "ordered_ids": ["l2-...", "l1-..."] }'

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

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

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию (пустой массив или некорректный UUID).
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
409reorder_duplicateВ ordered_ids есть повторяющиеся идентификаторы.
409reorder_incompleteСписок не совпадает с полным набором уроков модуля.
PATCH/api/v1/t/lessons/:id/cover-image

Установить обложку урока

AdminCurator JWT право: manage_courses

Привязывает к уроку медиа-актив в качестве обложки. cover_image_id должен ссылаться на существующий актив той же школы.

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

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

Тело запроса

ПараметрТипОписание
cover_image_idобяз.uuidИдентификатор медиа-актива для обложки.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/lessons/l1a2b3c4-.../cover-image \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "cover_image_id": "a9b8c7d6-..." }'

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

200 OK · application/json
{
  "success": true,
  "data": { "cover_image_id": "a9b8c7d6-..." }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
400invalid_cover_imageМедиа-актив не найден или принадлежит другой школе.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404lesson_not_foundУрок не найден.
GET/api/v1/t/lessons/:id/groups

Группы доступа урока

AdminCurator JWT право: manage_courses

Возвращает список UUID групп, которым ограничен доступ к уроку. Пустой массив означает, что урок доступен всем зачисленным студентам.

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

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

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

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

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

200 OK · application/json
{
  "success": true,
  "data": { "group_ids": ["g1a2b3c4-...", "g2b3c4d5-..."] }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404lesson_not_foundУрок не найден.
PUT/api/v1/t/lessons/:id/groups

Задать группы доступа урока

AdminCurator JWT право: manage_courses

Перезаписывает список групп, которым доступен урок. Если задана хотя бы одна группа — урок виден только студентам из этих групп; пустой массив снимает все ограничения (урок снова доступен всем зачисленным).

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

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

Тело запроса

ПараметрТипОписание
group_idsобяз.string[]Список UUID групп (пустой массив — снять ограничения).

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

cURL
curl -X PUT https://api.edumentor.kz/api/v1/t/lessons/l1a2b3c4-.../groups \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "group_ids": ["g1a2b3c4-..."] }'

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

200 OK · application/json
{
  "success": true,
  "data": { "group_ids": ["g1a2b3c4-..."] }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
400invalid_request_bodyТело запроса не прошло валидацию.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404lesson_not_foundУрок не найден.
POST/api/v1/t/lessons/:id/blocks

Создать контент-блок

AdminCurator JWT право: manage_courses

Добавляет в урок контент-блок. Поле type — один из: video, iframe, text, pdf, quiz_ref, assignment_ref, exam_ref (тип неизменяем после создания). is_optional=true оставляет блок доступным, но исключает его из прогресса и списка последовательных предпосылок. payload — объект, форма которого зависит от типа (см. ниже). FK-колонки media_asset_id (для video/pdf), quiz_id (для quiz_ref), assignment_id (для assignment_ref), exam_id (для exam_ref) обязательны для соответствующих типов. Экзамен в блоке exam_ref доступен ученикам, записанным на курс, и засчитывает блок после сдачи. Позиция назначается автоматически.

Формы payload по типам: video → { title, duration_seconds, alt_text } + media_asset_id; iframe → { url, title } (provider вычисляется сервером, url канонизируется и проверяется по allowlist хостов); text → { html } (HTML санируется через bluemonday UGCPolicy); pdf → { title, total_pages } + media_asset_id; quiz_ref → {} + quiz_id; assignment_ref → {} + assignment_id; exam_ref → {} + exam_id. Для video-блоков duration_seconds и для pdf-блоков total_pages можно НЕ указывать — бэкенд извлечёт их автоматически после загрузки медиа; если клиент прислал значение (> 0), используется оно (приоритет у клиента).

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

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

Тело запроса

ПараметрТипОписание
typeобяз.stringТип блока: video, iframe, text, pdf, quiz_ref, assignment_ref или exam_ref.Пример: video
payloadобяз.objectСодержимое блока; форма зависит от type (см. примечание).
is_optionalопц.booleanНе требовать завершение блока. По умолчанию false.
media_asset_idопц.uuidМедиа-актив. Обязателен для type=video и type=pdf.
quiz_idопц.uuidТест. Обязателен для type=quiz_ref.
assignment_idопц.uuidЗадание. Обязателен для type=assignment_ref.
exam_idопц.uuidПробный ЕНТ. Обязателен для type=exam_ref.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/lessons/l1a2b3c4-.../blocks \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "video",
    "is_optional": false,
    "media_asset_id": "a9b8c7d6-...",
    "payload": {
      "title": "Лекция 1",
      "duration_seconds": 615,
      "alt_text": "Видеолекция о синтаксисе Go"
    }
  }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "b1a2b3c4-...",
    "lesson_id": "l1a2b3c4-...",
    "tenant_id": "8f3b1c2a-...",
    "type": "video",
    "position": 0,
    "payload": {
      "title": "Лекция 1",
      "duration_seconds": 615,
      "alt_text": "Видеолекция о синтаксисе Go"
    },
    "media_asset_id": "a9b8c7d6-...",
    "created_at": "2026-05-28T10:20:00Z",
    "updated_at": "2026-05-28T10:20:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло базовую валидацию.
400unknown_block_typetype не входит в список допустимых.
400invalid_content_block_payloadpayload не соответствует объявленному типу или нарушено соответствие тип↔FK.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404lesson_not_foundУрок не найден.
GET/api/v1/t/lessons/:id/blocks

Список контент-блоков урока

Tenant JWT

Возвращает контент-блоки урока в порядке position. Для video/pdf блоков добавляется свежий presigned-URL (media_url). Для блоков quiz_ref/assignment_ref встраивается полная карточка теста (quiz) или задания (assignment) — чтобы клиент отрисовал содержимое одним запросом. Поле payload — типизированный объект (форма зависит от type). Поле data — массив блоков.

Для учеников при включённом sequential_required обязательный блок блокируется, пока не завершён предыдущий обязательный видимый блок. Необязательные блоки пропускаются при расчёте этой цепочки. Заблокированные блоки возвращаются с locked: true, а поля payload/media_url/quiz_id/assignment_id/media_asset_id вырезаются на сервере. Для админа/куратора последовательный гейтинг не применяется. Расписание работает независимо: оно может закрыть и необязательный блок, добавляя locked_reason и границы окна.

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

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

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

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

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

200 OK · application/json
{
  "success": true,
  "data": [
    {
      "id": "b1a2b3c4-...",
      "lesson_id": "l1a2b3c4-...",
      "type": "video",
      "position": 0,
      "payload": {
        "title": "Лекция 1",
        "duration_seconds": 615,
        "alt_text": "Видеолекция о синтаксисе Go"
      },
      "media_asset_id": "a9b8c7d6-...",
      "media_url": "https://minio.edumentor.kz/...&X-Amz-Signature=...",
      "locked": false
    },
    {
      "id": "b2a2b3c4-...",
      "lesson_id": "l1a2b3c4-...",
      "type": "quiz_ref",
      "position": 1,
      "locked": true,
      "locked_summary": {
        "title": "Итоговый тест",
        "question_count": 10
      }
    }
  ]
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
GET/api/v1/t/blocks/:id

Получить контент-блок

Tenant JWT

Возвращает один контент-блок с типизированным payload. Для video/pdf добавляется свежий presigned-URL (media_url). Для quiz_ref/assignment_ref встраивается полная карточка теста (quiz) или задания (assignment), включая их screenshot_protection_enabled.

Если ученику блок недоступен по последовательному гейтингу — возвращается 403 block_locked. Если блок недоступен по расписанию (урок вне окна дат, и правило «первой попытки» не применимо) — 403 schedule_window_closed. Для админа/куратора гейтинг не применяется.

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

ПараметрТипОписание
idобяз.uuidИдентификатор контент-блока.

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "b1a2b3c4-...",
    "lesson_id": "l1a2b3c4-...",
    "type": "text",
    "position": 1,
    "payload": { "html": "<p>Конспект урока.</p>" },
    "locked": false
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403block_lockedУченику блок недоступен по правилам последовательного прохождения.
403schedule_window_closedУрок блока вне окна доступа по расписанию (и правило «первой попытки» не применимо).
404block_not_foundБлок не найден.
PATCH/api/v1/t/blocks/:id

Обновить контент-блок

AdminCurator JWT право: manage_courses

Частичное обновление блока. Тип блока неизменяем — менять можно payload, media_asset_id и is_optional. payload валидируется и санируется как при создании, исходя из исходного типа блока. Изменение обязательности сразу пересчитывает снимки прогресса активных учеников.

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

ПараметрТипОписание
idобяз.uuidИдентификатор контент-блока.

Тело запроса

ПараметрТипОписание
payloadопц.objectНовое содержимое блока; форма зависит от исходного типа.
media_asset_idопц.uuidНовый медиа-актив (для video/pdf).
is_optionalопц.booleantrue — блок можно пропустить без влияния на прогресс и разблокировку.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/blocks/b1a2b3c4-... \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "payload": { "html": "<p>Обновлённый конспект.</p>" } }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "b1a2b3c4-...",
    "lesson_id": "l1a2b3c4-...",
    "type": "text",
    "position": 1,
    "payload": { "html": "<p>Обновлённый конспект.</p>" },
    "updated_at": "2026-05-28T11:30:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло базовую валидацию.
400invalid_content_block_payloadpayload не соответствует типу блока.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
404block_not_foundБлок не найден.
DELETE/api/v1/t/blocks/:id

Удалить контент-блок

AdminCurator JWT право: manage_courses

Мягкое удаление контент-блока (soft delete). Успех — 204 No Content без тела.

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

ПараметрТипОписание
idобяз.uuidИдентификатор контент-блока.

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

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

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

200 OK · application/json
204 No Content

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
PATCH/api/v1/t/lessons/:id/reorder-blocks

Переупорядочить контент-блоки

AdminCurator JWT право: manage_courses

Задаёт новый порядок контент-блоков внутри урока (drag-and-drop). Массив ordered_ids должен содержать полный набор идентификаторов блоков урока без дубликатов и пропусков.

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

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

Тело запроса

ПараметрТипОписание
ordered_idsобяз.string[]Полный список UUID блоков в желаемом порядке (минимум 1).

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/lessons/l1a2b3c4-.../reorder-blocks \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "ordered_ids": ["b2-...", "b1-...", "b3-..."] }'

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

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

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию (пустой массив или некорректный UUID).
401unauthorizedТокен отсутствует, истёк или недействителен.
403forbiddenНет права manage_courses.
409reorder_duplicateВ ordered_ids есть повторяющиеся идентификаторы.
409reorder_incompleteСписок не совпадает с полным набором блоков урока.