TenantAdminCurator

Тесты

Авторинг тестов и вопросов (CRUD + переупорядочивание), а также проверка попыток куратором: список попыток, детальный разбор и выставление баллов за свободные ответы.

POST/api/v1/t/quizzes

Создать тест

AdminCurator JWT право: manage_courses

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

Тело запроса

ПараметрТипОписание
titleобяз.stringНазвание теста (2–255 символов).
descriptionопц.stringОписание (до 10000 символов).
max_attemptsопц.integerМаксимум попыток (1–1000). Не задано — без ограничения.
time_limit_secondsопц.integerЛимит времени в секундах (10–86400). Не задано — без лимита.
passing_scoreопц.integerПроходной балл в процентах (1–100). Не задано — без понятия «зачёт/незачёт».
retake_policyопц.stringКакая попытка считается зачётной: best или last. По умолчанию best.
default_scoring_modeопц.stringРежим начисления баллов по умолчанию: all_or_nothing или partial. По умолчанию all_or_nothing.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/quizzes \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Тест по алгебре",
    "max_attempts": 3,
    "time_limit_seconds": 1800,
    "passing_score": 60,
    "retake_policy": "best",
    "default_scoring_mode": "all_or_nothing"
  }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "9c1f2e3a-...",
    "tenant_id": "8f3b1c2a-...",
    "title": "Тест по алгебре",
    "description": "",
    "max_attempts": 3,
    "time_limit_seconds": 1800,
    "passing_score": 60,
    "retake_policy": "best",
    "default_scoring_mode": "all_or_nothing",
    "questions": [],
    "course_refs": [],
    "published_via_course": false,
    "exam_refs": [],
    "published_via_exam": false,
    "created_at": "2026-05-28T10:00:00Z",
    "updated_at": "2026-05-28T10:00:00Z"
  }
}

Ошибки

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

Список тестов

Tenant JWT

Возвращает постраничный список тестов школы с агрегатами для кураторской таблицы: число вопросов, количество попыток, средний балл, число ожидающих проверки, а также контексты использования в курсах и экзаменах. Доступен любому авторизованному участнику школы.

Query-параметры

ПараметрТипОписание
qопц.stringПоиск по названию.
statusопц.stringФильтр: all, published или draft.
course_idопц.uuidФильтр по курсу, где используется тест.
pageопц.integerНомер страницы (по умолчанию 1).Пример: 1
limitопц.integerРазмер страницы (1–100, по умолчанию 20).Пример: 20

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

cURL
curl "https://api.edumentor.kz/api/v1/t/quizzes?q=алгебра&page=1&limit=20" \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme"

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

200 OK · application/json
{
  "success": true,
  "data": {
    "quizzes": [
      {
        "id": "9c1f2e3a-...",
        "tenant_id": "8f3b1c2a-...",
        "title": "Тест по алгебре",
        "description": "",
        "question_count": 10,
        "attempts_count": 24,
        "avg_score_pct": 72,
        "pending_reviews_count": 3,
        "course_refs": [
          {
            "course_id": "...",
            "course_title": "Математика 9 класс",
            "course_status": "published",
            "module_id": "...",
            "module_title": "Модуль 1",
            "lesson_id": "...",
            "lesson_title": "Урок 3"
          }
        ],
        "published_via_course": true,
        "exam_refs": [],
        "published_via_exam": false,
        "created_at": "2026-05-28T10:00:00Z",
        "updated_at": "2026-05-28T10:00:00Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}

Ошибки

КодerrorКогда
400invalid_paramsНеверные значения page/limit.
400invalid_course_idcourse_id не является корректным UUID.
401unauthorizedТокен отсутствует или недействителен.
GET/api/v1/t/quizzes/:id

Получить тест

Tenant JWT

Возвращает детальный тест с вложенными вопросами, ответами и сопоставлениями. Куратор/админ получают полный вид с правильными ответами и метаданными начисления баллов.

Для роли student отдаётся редактированный вид без правильных ответов: убраны is_correct, correct_text, scoring_mode, passing_score, retake_policy. Поле points (баллы за вопрос) теперь присутствует и в студенческом виде — чтобы показать «N баллов» при прохождении. Для вопросов типа matching правый столбец перемешан, а его опции получают одноразовые токены вместо реальных id, чтобы не раскрыть верные пары.

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

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

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "9c1f2e3a-...",
    "tenant_id": "8f3b1c2a-...",
    "title": "Тест по алгебре",
    "description": "",
    "max_attempts": 3,
    "time_limit_seconds": 1800,
    "passing_score": 60,
    "retake_policy": "best",
    "default_scoring_mode": "all_or_nothing",
    "questions": [
      {
        "id": "a1b2c3d4-...",
        "type": "single",
        "prompt": "Сколько будет 2+2?",
        "position": 0,
        "points": "1.00",
        "scoring_mode": null,
        "answers": [
          { "id": "...", "text": "4", "is_correct": true, "position": 0 },
          { "id": "...", "text": "5", "is_correct": false, "position": 1 }
        ],
        "correct_text": [],
        "matches": []
      }
    ],
    "course_refs": [],
    "published_via_course": false,
    "exam_refs": [],
    "published_via_exam": false,
    "created_at": "2026-05-28T10:00:00Z",
    "updated_at": "2026-05-28T10:00:00Z"
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует или недействителен.
404quiz_not_foundТест не найден или принадлежит другой школе.
PATCH/api/v1/t/quizzes/:id

Обновить тест

AdminCurator JWT право: manage_courses

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

Явное обнуление nullable-полей (например, снятие лимита попыток через max_attempts=null) в v1 не поддерживается.

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

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

Тело запроса

ПараметрТипОписание
titleопц.stringНазвание (2–255 символов).
descriptionопц.stringОписание (до 10000 символов).
max_attemptsопц.integerМаксимум попыток (1–1000).
time_limit_secondsопц.integerЛимит времени в секундах (10–86400).
passing_scoreопц.integerПроходной балл в процентах (1–100).
retake_policyопц.stringbest или last.
default_scoring_modeопц.stringall_or_nothing или partial.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/quizzes/9c1f2e3a-... \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Тест по алгебре (обновлён)", "passing_score": 70 }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "9c1f2e3a-...",
    "title": "Тест по алгебре (обновлён)",
    "passing_score": 70,
    "questions": []
  }
}

Ошибки

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

Удалить тест

AdminCurator JWT право: manage_courses

Мягко удаляет тест. Возвращает 204 без тела при успехе.

Если тест используется как секция экзамена, удаление блокируется кодом 409 quiz_referenced_by_exam.

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

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

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

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

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

200 OK · application/json
HTTP/1.1 204 No Content

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует или недействителен.
403forbiddenНет права manage_courses.
404quiz_not_foundТест не найден или принадлежит другой школе.
409quiz_referenced_by_examТест используется как секция экзамена и не может быть удалён.
POST/api/v1/t/quizzes/:id/questions

Добавить вопрос

AdminCurator JWT право: manage_courses

Создаёт вопрос в тесте. Поле type задаёт форму вопроса: single (один правильный ответ), multiple (несколько), free_text (свободный текст), matching (сопоставление). Варианты ответов/сопоставления добавляются отдельно через обновление вопроса.

points — десятичная строка («1.00», «2.5»), должна быть > 0; не задано — DB-дефолт «1.0». scoring_mode partial допустим только для типов multiple и matching.

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

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

Тело запроса

ПараметрТипОписание
typeобяз.stringТип вопроса: single, multiple, free_text или matching.
promptобяз.stringФормулировка вопроса (1–2000 символов).
pointsопц.stringВес вопроса как десятичная строка (> 0). По умолчанию «1.0».
scoring_modeопц.stringall_or_nothing или partial (переопределяет дефолт теста).

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/quizzes/9c1f2e3a-.../questions \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "single",
    "prompt": "Сколько будет 2+2?",
    "points": "1.0"
  }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "a1b2c3d4-...",
    "type": "single",
    "prompt": "Сколько будет 2+2?",
    "position": 0,
    "points": "1.00",
    "scoring_mode": null,
    "answers": [],
    "correct_text": [],
    "matches": []
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию (неизвестный type, пустой prompt и т.д.).
400invalid_id_formatid теста не является корректным UUID.
401unauthorizedТокен отсутствует или недействителен.
403forbiddenНет права manage_courses.
404quiz_not_foundТест не найден или принадлежит другой школе.
PATCH/api/v1/t/quizzes/:id/reorder-questions

Переупорядочить вопросы

AdminCurator JWT право: manage_courses

Атомарно переписывает позиции всех вопросов теста. ordered_ids должен быть точным множеством id всех активных вопросов теста в желаемом порядке.

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

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

Тело запроса

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

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/quizzes/9c1f2e3a-.../reorder-questions \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{ "ordered_ids": ["a1b2c3d4-...", "e5f6a7b8-..."] }'

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

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

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
400invalid_id_formatid теста не является корректным UUID.
401unauthorizedТокен отсутствует или недействителен.
403forbiddenНет права manage_courses.
404quiz_not_foundТест не найден или принадлежит другой школе.
409reorder_duplicateВ списке есть повторяющиеся id.
409reorder_incompleteСписок не совпадает с полным множеством вопросов теста.
PATCH/api/v1/t/quiz-questions/:id

Обновить вопрос

AdminCurator JWT право: manage_courses

Частично обновляет вопрос. Поля answers, correct_text и matches имеют REPLACE-семантику: при передаче существующая коллекция полностью переписывается. Тип вопроса после создания неизменяем.

Форма payload зависит от типа вопроса: answers (text + is_correct) — для single/multiple; correct_text (массив строк) — для free_text; matches (left_text опционально, right_text обязателен и уникален) — только для matching. scoring_mode="" (пустая строка) сбрасывает переопределение к дефолту теста.

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

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

Тело запроса

ПараметрТипОписание
promptопц.stringФормулировка (1–2000 символов).
answersопц.objectМассив вариантов { text (≤500), is_correct } — для single/multiple.
correct_textопц.string[]Массив эталонных строк (каждая ≤500) — для free_text.
pointsопц.stringВес вопроса как десятичная строка (> 0).
scoring_modeопц.stringall_or_nothing, partial или "" (сброс к дефолту теста).
matchesопц.objectМассив пар { left_text? (≤500), right_text (1–500) } — только для matching, минимум 1.

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

cURL
curl -X PATCH https://api.edumentor.kz/api/v1/t/quiz-questions/a1b2c3d4-... \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Сколько будет 2+2?",
    "answers": [
      { "text": "4", "is_correct": true },
      { "text": "5", "is_correct": false }
    ]
  }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "id": "a1b2c3d4-...",
    "type": "single",
    "prompt": "Сколько будет 2+2?",
    "position": 0,
    "points": "1.00",
    "scoring_mode": null,
    "answers": [
      { "id": "...", "text": "4", "is_correct": true, "position": 0 },
      { "id": "...", "text": "5", "is_correct": false, "position": 1 }
    ],
    "correct_text": [],
    "matches": []
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
400invalid_id_formatid не является корректным UUID.
400answers_not_allowed_for_free_textДля free_text нельзя задавать answers.
400answers_not_allowed_for_matchingДля matching нельзя задавать answers.
400correct_text_not_allowed_for_choiceДля single/multiple нельзя задавать correct_text.
400single_choice_multiple_correctДля single задано более одного правильного варианта.
400matches_not_allowed_for_non_matching_questionmatches допустимы только для типа matching.
401unauthorizedТокен отсутствует или недействителен.
403forbiddenНет права manage_courses.
404question_not_foundВопрос не найден или принадлежит другой школе.
DELETE/api/v1/t/quiz-questions/:id

Удалить вопрос

AdminCurator JWT право: manage_courses

Мягко удаляет вопрос. Возвращает 204 без тела при успехе.

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

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

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

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

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

200 OK · application/json
HTTP/1.1 204 No Content

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует или недействителен.
403forbiddenНет права manage_courses.
404question_not_foundВопрос не найден или принадлежит другой школе.
GET/api/v1/t/quizzes/:id/attempts

Список попыток теста

AdminCurator JWT право: view_analytics

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

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

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

Query-параметры

ПараметрТипОписание
pageопц.integerНомер страницы (по умолчанию 1).Пример: 1
limitопц.integerРазмер страницы (1–100, по умолчанию 20).Пример: 20

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "attempts": [
      {
        "id": "11112222-...",
        "student_id": "33334444-...",
        "student_email": "student@acme.kz",
        "student_first_name": "Алия",
        "student_last_name": "Касымова",
        "score": "8.00",
        "max_score": "10.00",
        "score_pct": 80,
        "passed": true,
        "started_at": "2026-05-28T09:00:00Z",
        "submitted_at": "2026-05-28T09:25:00Z",
        "duration_seconds": 1500,
        "has_pending_review": false
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid теста не является корректным UUID.
400invalid_paramsНеверные значения page/limit.
401unauthorizedТокен отсутствует или недействителен.
403forbiddenНет права view_analytics.
404quiz_not_foundТест не найден или принадлежит другой школе.
GET/api/v1/t/quiz-attempts/:id

Детальный разбор попытки

AdminCurator JWT право: view_analytics

Полный кураторский разбор попытки: заголовок попытки, авторский вид теста и по каждому вопросу — авторские правильные ответы плюс отправленный учеником payload и вердикт проверки. Используется для проверки free_text и анализа автооценки.

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

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

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

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

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

200 OK · application/json
{
  "success": true,
  "data": {
    "attempt": {
      "id": "11112222-...",
      "student_id": "33334444-...",
      "student_email": "student@acme.kz",
      "score": "8.00",
      "max_score": "10.00",
      "score_pct": 80,
      "passed": true,
      "started_at": "2026-05-28T09:00:00Z",
      "submitted_at": "2026-05-28T09:25:00Z",
      "duration_seconds": 1500,
      "has_pending_review": true
    },
    "quiz": {
      "id": "9c1f2e3a-...",
      "title": "Тест по алгебре",
      "passing_score": 60,
      "questions": []
    },
    "questions": [
      {
        "id": "a1b2c3d4-...",
        "type": "free_text",
        "prompt": "Объясните теорему Пифагора.",
        "position": 0,
        "points": "2.00",
        "scoring_mode": null,
        "answers": [],
        "correct_text": ["a^2 + b^2 = c^2"],
        "matches": [],
        "student_answer_payload": { "text": "Сумма квадратов катетов..." },
        "is_correct": false,
        "awarded_points": "0.00"
      }
    ]
  }
}

Ошибки

КодerrorКогда
400invalid_id_formatid не является корректным UUID.
401unauthorizedТокен отсутствует или недействителен.
403forbiddenНет права view_analytics.
404attempt_not_foundПопытка не найдена, принадлежит другой школе или обёрнута экзаменом.
POST/api/v1/t/quiz-attempts/:id/grade

Выставить баллы за попытку

AdminCurator JWT право: manage_courses

Сохраняет баллы и комментарии куратора по вопросам типа free_text и пересчитывает итоговый балл попытки (сумма awarded_points по всем ответам) и флаг passed, если у теста задан проходной балл. Баллы для автооцениваемых вопросов игнорируются — их нельзя переопределить.

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

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

Тело запроса

ПараметрТипОписание
gradesобяз.objectМассив { question_id (uuid), awarded_points (десятичная строка), feedback? (≤4000) }; минимум 1.

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

cURL
curl -X POST https://api.edumentor.kz/api/v1/t/quiz-attempts/11112222-.../grade \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{
    "grades": [
      {
        "question_id": "a1b2c3d4-...",
        "awarded_points": "2.0",
        "feedback": "Верно по сути."
      }
    ]
  }'

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

200 OK · application/json
{
  "success": true,
  "data": {
    "attempt": {
      "id": "11112222-...",
      "student_id": "33334444-...",
      "student_email": "student@acme.kz",
      "score": "10.00",
      "max_score": "10.00",
      "score_pct": 100,
      "passed": true,
      "started_at": "2026-05-28T09:00:00Z",
      "submitted_at": "2026-05-28T09:25:00Z",
      "duration_seconds": 1500,
      "has_pending_review": false
    }
  }
}

Ошибки

КодerrorКогда
400invalid_request_bodyТело запроса не прошло валидацию.
400invalid_id_formatid не является корректным UUID.
400attempt_not_submittedПопытка не завершена — проверять можно только отправленные попытки.
400invalid_gradeawarded_points не число, вне диапазона [0, max] или указан неизвестный question_id.
401unauthorizedТокен отсутствует или недействителен.
403forbiddenНет права manage_courses.
404attempt_not_foundПопытка не найдена, принадлежит другой школе или обёрнута экзаменом.