Quiz / Exam JSON & ZIP export, bulk question import
This file documents the bulk import / export API surface for Quizzes and Exams — extracted from the main Quizzes & Exams endpoints page so it can be consumed on its own. It covers the export of single quizzes/exams to JSON, bulk export of every quiz or exam to a ZIP archive, and the destructive question/option import (with images). All endpoints are served by theExport_Import_Quizzes_Exams_Controller.
For the data model, the quiz/exam CRUD endpoints, and authoring questions through the API, see Quizzes & Exams endpoints. The student attempt lifecycle (start / finish / save score / show answers) is documented in Quizzes & Exams progress tracking.
Table of Contents
- Overview
- Base URL and Headers
- Export / Import Endpoints
- 3.1 Export Quiz JSON
- 3.2 Import Questions & Options (with images)
- 3.3 Export Exam JSON
- 3.4 Export All Exams ZIP
- 3.5 Export All Quizzes ZIP
- Business Rules & Edge Cases
- History Tracking Actions
- Related Files
1. Overview
AQuiz is a container of questions. There is one Eloquent model and one quizzes table behind everything here — what a row is depends entirely on whether its training_id and chapter_id foreign keys are set. There is no type column and no separate “exam” table. This distinction decides which export endpoint you use:
- Quiz (chapter quiz or training/final quiz) — exported via the
/api/quizzes/...routes (§3.1, §3.5). Quiz JSON files additionally carry abelongs_toblock naming the parent training/chapter. - Exam (standalone, both FKs
null) — exported via the/api/exams/...routes (§3.3, §3.4).
404 — each endpoint filters by its own scope (isQuiz() / isExam()).
The question/option import (§3.2) is shared by both: it targets any quizzes row by id and destructively replaces its questions and options.
2. Base URL and Headers
Base API URL:company_idmust be passed explicitly on most endpoints and is validated against thecompaniestable.App\Http\Middleware\CompanyPolicyMiddlewareenforces that the value matches the authenticated user’s owncompany_id(super admins bypass this check).
3. Export / Import Endpoints
Controller:app/Http/Controllers/Export_Import_Quizzes_Exams_Controller.php.
3.1 Export Quiz JSON
GET /api/quizzes/{quizId}/export-json
Authorize: view on the quiz. Only isQuiz() rows (linked to training/chapter) for the authenticated user’s company_id are exported. Passing an exam id returns 404.
Example Request:
application/json file download.
- Filename format:
{slug(label)}-quiz-{Y-m-d-H-i}.json - Example filename:
chapter-1-final-quiz-2026-05-16-14-30.json
Error Responses:belongs_to(quiz exports only). Every exported quiz JSON includes abelongs_toblock naming the parent training and/or chapter the quiz lives under — so the file is self-describing even outside the database. Resolution:
- Quiz linked directly to a training (
training_idset) →belongs_to.trainingis filled,belongs_to.chapterisnull.- Quiz linked to a chapter (
chapter_idset) →belongs_to.chapteris filled, andbelongs_to.trainingis resolved through the chapter (chapter.training_id), even when the quiz row itself has notraining_id.- Each entry exposes
id,label, and (for training)slug;chapteralso exposes itstraining_id. This block is not present in exam exports (§3.3 / §3.4) — exams are standalone and belong to no training/chapter.
3.2 Import Questions & Options (with images)
POST /api/quizzes/{quizId}/import-questions-images
Content-Type: multipart/form-data — in Postman, set Body → form-data.
Form-Data Fields:
Postman Setup:
- Set method to
POST, URL to{{base_url}}/api/quizzes/5/import-questions-images - Under Headers: add
Authorization: Bearer {{token}}andAccept: application/json(do not manually setContent-Typefor form-data — Postman sets it automatically with the boundary) - Under Body, select form-data
- Add row: Key =
company_id, Type = Text, Value =1 - Add row: Key =
questions_file, Type = File, Value = select your.jsonfile
questions_import.json and upload:
Behavior:image_url— when provided and not null, the server downloads the image via HTTP and registers it as aFilerecord. The URL must be publicly reachable from the server. Set tonullto skip image import for a question.
- Authorize
updateon the quiz. - Verify
company_idmatches the quiz’scompany_id. - Destructive replace — deletes all existing questions and options on the quiz.
- Creates questions, options, and option items (drag & drop only) from the file.
- Downloads any
image_urlvalues, stores files, and links them to the question. - History:
imported_questions_with_images.
3.3 Export Exam JSON
GET /api/exams/export-json
Both query params are required. The quiz must pass the isExam() scope (training_id = null and chapter_id = null). Passing a quiz (non-exam) id returns 404.
Query Parameters:
Example Request:
application/json file download.
- Filename format:
{slug(label)}-exam-{d-m-Y-H-i}.json - Example filename:
advanced-python-exam-16-05-2026-14-30.json - File content has the same structure as §3.1, minus the
belongs_toblock (exams are standalone and belong to no training/chapter).
3.4 Export All Exams ZIP
GET /api/exams/export-all-json
Exports every isExam() row for the company as a single ZIP archive, with one JSON file per exam.
Query Parameters:
Example Request:
- ZIP filename format:
exams_export_{d-m-Y-H-i}.zip - Each exam inside:
{slug(label)}-exam-{d-m-Y-H-i}.json
Large companies should expect this to be heavy. Make sure your server’sphp.iniallows large uploads:upload_max_filesize = 300M,post_max_size = 300M,memory_limit = 512M,max_execution_time = 600.
3.5 Export All Quizzes ZIP
GET /api/quizzes/export-all-json
Exports every isQuiz() row for the company (i.e. quizzes linked to a training and/or a chapter — not standalone exams) as a single ZIP archive, with one JSON file per quiz. This is the quiz-side counterpart of §3.4 (Export All Exams ZIP).
Query Parameters:
Example Request:
- ZIP filename format:
quizzes_export_{d-m-Y-H-i}.zip - Each quiz inside:
{slug(label)}-quiz-{d-m-Y-H-i}.json - Each JSON file has the same structure as §3.1 — including the
belongs_toblock that records the parent training and/or chapter the quiz belongs to.
Like §3.4, this can be heavy for large companies — give PHP enough headroom (memory_limit = 512M,max_execution_time = 600).
4. Business Rules & Edge Cases
- Scope filtering decides 404s. Quiz export routes (
/api/quizzes/...) only matchisQuiz()rows; exam export routes (/api/exams/...) only matchisExam()rows. Cross-using them returns404. belongs_tois quiz-only. Only quiz JSON exports (§3.1, §3.5) carry thebelongs_toblock. For a chapter quiz (onlychapter_idset), the training is resolved through the chapter. Exam exports never include it.- Question import is destructive. §3.2 deletes all existing questions and options on the target quiz before re-creating them from the file — there is no merge mode. Back up via §3.1 first if needed.
company_idmust match. The import verifies the suppliedcompany_idequals the quiz’s owncompany_id(422on mismatch), on top of the tenantCompanyPolicyMiddlewarecheck.- Image import requires public URLs.
image_urlvalues are fetched server-side over HTTP; URLs unreachable from the server cause the import to fail (500). - Bulk ZIP exports are heavy. Both §3.4 and §3.5 build the entire archive in memory/temp before streaming; ensure PHP
memory_limit/max_execution_timeare generous for large catalogs.
5. History Tracking Actions
Calls toinsert_in_history_table($action, $resource_id, $table) made by this controller:
The export endpoints (§3.1, §3.3, §3.4, §3.5) are read-only and do not write history rows.
6. Related Files
app/Http/Controllers/Export_Import_Quizzes_Exams_Controller.php— the controller behind every endpoint in this file.routes/api.php—Export_Import_Quizzes_Exams_Controllerroute group.app/Models/Quiz.php— parent model (scopesisQuiz,isExam;training()/chapter()relations used to buildbelongs_to).app/Models/QuizQuestion.php,QuizQuestionType.php,QuizQuestionOption.php,QuizQuestionOptionItem.php— the structures serialized into / parsed from the JSON.app/Models/Training.php,app/Models/Chapter.php— sources for thebelongs_totraining/chapter context.- Quizzes & Exams endpoints — data model, quiz/exam CRUD, and question authoring.
- Quizzes & Exams progress tracking — student attempt lifecycle.