Kaltura Interactive Video Quiz API¶
The quiz system turns video entries into interactive assessments.
It uses cue points for questions and answers, with a configuration layer
on the entry and a user-entry service for tracking attempts and scores.
Base URL: $KALTURA_SERVICE_URL (e.g., https://www.kaltura.com/api_v3)
Auth: KS via ks parameter (admin KS with disableentitlement for full access)
Format: format=1 for JSON responses
Services: quiz_quiz, cuepoint_cuepoint, userEntry, report
Prerequisite: Cue Points Hub for base cue point concepts and shared CRUD
1. When to Use¶
- Interactive learning — Add assessments directly inside training videos so learners answer questions without leaving the player.
- Knowledge evaluation — Measure learner comprehension with scored quizzes that track attempts, correctness, and completion rates.
- LMS quiz integration — Programmatically create and manage video-based quizzes from an external Learning Management System, syncing scores and reports back to the LMS.
- Certification and compliance — Gate course completion behind passing quiz scores, supporting multiple retakes and configurable scoring models.
- Content engagement — Increase viewer attention and retention by interspersing questions, reflection points, and branching navigation throughout video content.
2. Prerequisites¶
- KS (Kaltura Session): Admin KS (type=2) with
disableentitlementfor creating and managing quizzes. Viewer interactions (starting attempts, submitting answers) use a USER KS (type=0) scoped to the entry. - Cue Points plugin: The
cuePointserver plugin must be enabled on the account (enabled by default on most accounts). - Quiz plugin: The
quizserver plugin must be enabled on the account. - Player IVQ plugin: To render quizzes during playback, enable the
ivqandkalturaCuepointsplugins in the player configuration. - Session management: See Session Guide for KS generation and privilege scoping.
3. Quiz Lifecycle¶
quiz.add (mark entry as quiz)
→ cuePoint.add (add KalturaQuestionCuePoint for each question)
→ userEntry.add (viewer starts attempt → KalturaQuizUserEntry)
→ cuePoint.add (viewer answers → KalturaAnswerCuePoint per question)
→ userEntry.submitQuiz (calculate score)
→ report.getTable (pull reports)
An entry becomes a quiz when you call quiz.add. Questions are cue points positioned on the timeline. Viewers start an attempt, answer questions as playback progresses, and submit for scoring. The system auto-calculates correctness and computes scores across attempts.
4. Mark an Entry as a Quiz¶
Service: quiz_quiz
curl -X POST "$KALTURA_SERVICE_URL/service/quiz_quiz/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "entryId=1_abc123" \
-d "quiz[objectType]=KalturaQuiz" \
-d "quiz[showResultOnAnswer]=1" \
-d "quiz[showCorrectAfterSubmission]=1" \
-d "quiz[allowAnswerUpdate]=1" \
-d "quiz[showGradeAfterSubmission]=1" \
-d "quiz[attemptsAllowed]=3" \
-d "quiz[scoreType]=1"
If the entry already has a quiz configuration, quiz.add returns PROVIDED_ENTRY_IS_ALREADY_A_QUIZ — use quiz.update instead.
5. Quiz Configuration (KalturaQuiz)¶
| Field | Type | Description |
|---|---|---|
version |
int | Auto-incremented on update (readonly) |
showResultOnAnswer |
int | Show correct/incorrect immediately (-1/0/1) |
showCorrectKeyOnAnswer |
int | Show correct answer key while answering |
allowAnswerUpdate |
int | Allow changing answers before submission |
showCorrectAfterSubmission |
int | Reveal correct answers after submit |
allowDownload |
int | Allow PDF download of quiz |
showGradeAfterSubmission |
int | Show score after submit |
attemptsAllowed |
int | Number of retakes allowed |
scoreType |
int | How to calculate final score across attempts |
6. Quiz Service Actions¶
| Action | Description |
|---|---|
add |
Mark entry as quiz with configuration |
get |
Get quiz config by entryId |
update |
Update quiz settings (increments version) |
list |
List quiz entries |
serve |
Download quiz as PDF |
getUrl |
Get PDF download URL (quizOutputType: 1=PDF) |
Deleting quiz attempts: Use userEntry.delete with the user entry ID to remove a quiz attempt.
7. Question Types¶
| Value | Name | Description |
|---|---|---|
| 1 | MULTIPLE_CHOICE_ANSWER | Single correct answer from options |
| 2 | TRUE_FALSE | True/false question |
| 3 | REFLECTION_POINT | Pause for reflection (no correct answer, not scored) |
| 4 | MULTIPLE_ANSWER_QUESTION | Multiple correct answers |
| 5 | FILL_IN_BLANK | Fill in the blank |
| 6 | HOT_SPOT | Hotspot on video frame |
| 7 | GO_TO | Navigation/branching point |
| 8 | OPEN_QUESTION | Free-text open-ended question |
7.1 Multiple Choice (type=1)¶
curl -X POST "$KALTURA_SERVICE_URL/service/cuepoint_cuepoint/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "cuePoint[objectType]=KalturaQuestionCuePoint" \
-d "cuePoint[entryId]=1_abc123" \
-d "cuePoint[startTime]=30000" \
-d "cuePoint[question]=What design pattern separates object creation from usage?" \
-d "cuePoint[questionType]=1" \
-d "cuePoint[hint]=Think about object creation" \
-d "cuePoint[explanation]=The Factory pattern delegates instantiation to subclasses" \
-d "cuePoint[optionalAnswers][0][key]=1" \
-d "cuePoint[optionalAnswers][0][text]=Factory" \
-d "cuePoint[optionalAnswers][0][isCorrect]=1" \
-d "cuePoint[optionalAnswers][0][weight]=1" \
-d "cuePoint[optionalAnswers][1][key]=2" \
-d "cuePoint[optionalAnswers][1][text]=Singleton" \
-d "cuePoint[optionalAnswers][1][isCorrect]=0" \
-d "cuePoint[optionalAnswers][1][weight]=1" \
-d "cuePoint[optionalAnswers][2][key]=3" \
-d "cuePoint[optionalAnswers][2][text]=Observer" \
-d "cuePoint[optionalAnswers][2][isCorrect]=0" \
-d "cuePoint[optionalAnswers][2][weight]=1"
7.2 True/False (type=2)¶
curl -X POST "$KALTURA_SERVICE_URL/service/cuepoint_cuepoint/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "cuePoint[objectType]=KalturaQuestionCuePoint" \
-d "cuePoint[entryId]=1_abc123" \
-d "cuePoint[startTime]=20000" \
-d "cuePoint[question]=The sky is blue." \
-d "cuePoint[questionType]=2" \
-d "cuePoint[optionalAnswers][0][key]=true" \
-d "cuePoint[optionalAnswers][0][text]=True" \
-d "cuePoint[optionalAnswers][0][isCorrect]=1" \
-d "cuePoint[optionalAnswers][1][key]=false" \
-d "cuePoint[optionalAnswers][1][text]=False" \
-d "cuePoint[optionalAnswers][1][isCorrect]=0"
7.3 Reflection Point (type=3)¶
Pauses for reflection — no correct answer, excluded from scoring:
curl -X POST "$KALTURA_SERVICE_URL/service/cuepoint_cuepoint/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "cuePoint[objectType]=KalturaQuestionCuePoint" \
-d "cuePoint[entryId]=1_abc123" \
-d "cuePoint[startTime]=25000" \
-d "cuePoint[question]=Pause and consider — what did you learn so far?" \
-d "cuePoint[questionType]=3" \
-d "cuePoint[excludeFromScore]=1"
7.4 Multiple Answer (type=4)¶
Multiple correct answers — viewer must select all correct options:
curl -X POST "$KALTURA_SERVICE_URL/service/cuepoint_cuepoint/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "cuePoint[objectType]=KalturaQuestionCuePoint" \
-d "cuePoint[entryId]=1_abc123" \
-d "cuePoint[startTime]=35000" \
-d "cuePoint[question]=Select ALL primary colors" \
-d "cuePoint[questionType]=4" \
-d "cuePoint[optionalAnswers][0][key]=r" \
-d "cuePoint[optionalAnswers][0][text]=Red" \
-d "cuePoint[optionalAnswers][0][isCorrect]=1" \
-d "cuePoint[optionalAnswers][1][key]=g" \
-d "cuePoint[optionalAnswers][1][text]=Green" \
-d "cuePoint[optionalAnswers][1][isCorrect]=0" \
-d "cuePoint[optionalAnswers][2][key]=b" \
-d "cuePoint[optionalAnswers][2][text]=Blue" \
-d "cuePoint[optionalAnswers][2][isCorrect]=1"
7.5 Open Question (type=8)¶
Free-text open-ended question — viewer types a response:
curl -X POST "$KALTURA_SERVICE_URL/service/cuepoint_cuepoint/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "cuePoint[objectType]=KalturaQuestionCuePoint" \
-d "cuePoint[entryId]=1_abc123" \
-d "cuePoint[startTime]=40000" \
-d "cuePoint[question]=Describe the main concept in your own words" \
-d "cuePoint[questionType]=8"
7.6 Fill in the Blank (type=5)¶
The viewer fills in a blank within the question text. The optionalAnswers array defines acceptable answers.
7.7 Hot Spot (type=6)¶
Hotspot on a video frame — the viewer clicks on a region. Position data is stored in partnerData.
7.8 Go To (type=7)¶
Navigation/branching point — used for adaptive quizzes where the next question depends on the answer.
8. Question Fields¶
| Field | Type | Description |
|---|---|---|
question |
string | Question text |
questionType |
int | Question type (see table above) |
optionalAnswers |
array | Array of answer choices |
hint |
string | Hint text shown to viewer |
explanation |
string | Explanation (hidden from non-editors) |
presentationOrder |
int | Display order |
excludeFromScore |
int | Exclude from score calculation (-1/0/1) |
Each optionalAnswer has: key (string), text (string), weight (float, default 1.0), isCorrect (int: -1/0/1).
Security: When a non-editor viewer retrieves question cue points, isCorrect on each answer option is returned as null and explanation is omitted. This prevents viewers from reading correct answers via the API.
9. Viewer Quiz Flow¶
Step 1 — Start attempt:
curl -X POST "$KALTURA_SERVICE_URL/service/userentry/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "userEntry[objectType]=KalturaQuizUserEntry" \
-d "userEntry[entryId]=1_abc123"
Returns a KalturaQuizUserEntry with id, status=1 (ACTIVE), and version (attempt number).
Step 2 — Submit answers:
For each question, create a KalturaAnswerCuePoint:
curl -X POST "$KALTURA_SERVICE_URL/service/cuepoint_cuepoint/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "cuePoint[objectType]=KalturaAnswerCuePoint" \
-d "cuePoint[entryId]=1_abc123" \
-d "cuePoint[parentId]=1_question_cp_id" \
-d "cuePoint[quizUserEntryId]=12345" \
-d "cuePoint[answerKey]=1"
The server auto-calculates isCorrect by comparing answerKey against the question's correct answer keys.
For open questions, use openAnswer instead of answerKey:
curl -X POST "$KALTURA_SERVICE_URL/service/cuepoint_cuepoint/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "cuePoint[objectType]=KalturaAnswerCuePoint" \
-d "cuePoint[entryId]=1_abc123" \
-d "cuePoint[parentId]=1_open_question_id" \
-d "cuePoint[quizUserEntryId]=12345" \
-d "cuePoint[openAnswer]=The main concept is dependency injection via factory pattern."
Step 3 — Submit quiz for scoring:
curl -X POST "$KALTURA_SERVICE_URL/service/userentry/action/submitQuiz" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "id=12345"
Returns the KalturaQuizUserEntry with score, calculatedScore, and status=quiz.3 (SUBMITTED).
10. Answer Fields¶
| Field | Type | Description |
|---|---|---|
parentId |
string | Question cue point ID (insert-only, required) |
quizUserEntryId |
string | User entry ID for this attempt (insert-only, required) |
answerKey |
string | Selected answer key |
openAnswer |
string | Free-text answer (max 1024 chars, for open questions) |
isCorrect |
int | Server-computed correctness (readonly) |
correctAnswerKeys |
array | Correct answer keys from the question (readonly) |
explanation |
string | Explanation copied from question (readonly) |
feedback |
string | Instructor feedback (max 1024 chars, admin-only write) |
Instructor feedback: Update an answer cue point with the feedback field. The update requires both entryId and quizUserEntryId in the request body.
11. Quiz User Entry¶
| Field | Type | Description |
|---|---|---|
score |
float | Raw quiz score (readonly) |
calculatedScore |
float | Score based on scoreType across attempts (readonly) |
feedback |
string | Overall instructor feedback (max 1024 chars) |
version |
int | Attempt number, 0-based (readonly) |
status |
string | 1=ACTIVE, 2=DELETED, quiz.3=SUBMITTED |
12. Scoring Models¶
| Value | Name | Description |
|---|---|---|
| 1 | HIGHEST | Best score across all attempts |
| 2 | LOWEST | Worst score across all attempts |
| 3 | LATEST | Most recent attempt |
| 4 | FIRST | First attempt only |
| 5 | AVERAGE | Average across all attempts |
13. Quiz Reports¶
Four report types accessible via report.getTable:
| reportType | Description |
|---|---|
quiz.QUIZ |
Per-question correct/wrong percentage |
quiz.QUIZ_USER_PERCENTAGE |
Per-user overall percentage |
quiz.QUIZ_AGGREGATE_BY_QUESTION |
Aggregated by specific question IDs |
quiz.QUIZ_USER_AGGREGATE_BY_QUESTION |
Per-user per-question breakdown |
curl -X POST "$KALTURA_SERVICE_URL/service/report/action/getTable" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "reportType=quiz.QUIZ" \
-d "reportInputFilter[objectType]=KalturaEndUserReportInputFilter" \
-d "reportInputFilter[entryIdIn]=1_abc123" \
-d "reportInputFilter[timeZoneOffset]=0" \
-d "objectIds=1_abc123" \
-d "pager[pageSize]=25"
The objectIds parameter must be set to the entry ID. The response contains header (comma-separated column names) and data (rows).
14. Player IVQ Plugin¶
The ivq player plugin renders quiz questions as overlays during playback:
All quiz behavior is driven by the quiz data on the entry (via quiz.get), not plugin config.
Quiz player flow:
1. Welcome screen (if showWelcomePage enabled) with available attempts
2. As playback reaches a question's startTime, video pauses and question overlay appears
3. If preventSeek is enabled, forward seeking is blocked (middleware intercepts setCurrentTime)
4. After all questions answered and video ends, submit screen appears
5. After submission, review screen shows score and correct answers (per quiz config)
Events: QuizStarted, QuizSkipped, QuestionAnswered, QuizSubmitted, QuizRetake
15. Quiz Generation via REACH / Content Lab¶
AI-powered quiz generation is available through two paths:
- REACH API — Order a
QUIZtask (serviceFeature=12) viaentryVendorTask.add. The AI engine analyzes video content and generates question cue points automatically. See REACH API. - Content Lab — The Content Lab widget provides a UI for AI quiz generation from video content. See Content Lab API.
Both paths produce standard KalturaQuestionCuePoint cue points — the same type created manually via the API.
16. Gamification Integration¶
Quiz completion events (quizSubmitted) can trigger gamification scoring rules — leaderboard points, badges for quiz completion, and certificates for passing scores. See Gamification API.
17. Searching Quiz Content¶
Quiz questions and answers are indexed in eSearch via KalturaESearchCuePointItem. Searchable fields include question, answers, hint, and explanation. See Cue Points Hub — eSearch Integration for query examples.
18. Error Handling¶
| Error | Cause | Fix |
|---|---|---|
PROVIDED_ENTRY_IS_ALREADY_A_QUIZ |
Calling quiz.add on entry with existing quiz config |
Use quiz.update to modify existing config |
PARENT_CUE_POINT_NOT_FOUND |
Invalid parentId on answer cue point |
Parent question must exist on the same entry |
CANNOT_APPROVE_TASK-like for quiz |
Submitting already-submitted quiz | Check userEntry.status before submission |
PROPERTY_VALIDATION_CANNOT_BE_NULL |
Answer without parentId or quizUserEntryId |
Both fields are required on answer creation |
USER_ENTRY_NOT_FOUND |
Invalid quizUserEntryId on answer |
Start an attempt with userEntry.add first |
19. Best Practices¶
- Quiz answer security is server-enforced. Non-editors cannot see
isCorrectorexplanationon question cue points — no client-side hiding needed. - Use
excludeFromScore=1for reflection points (type=3) and any question not meant for grading. - Check
userEntry.statusbefore submission. Already-submitted quizzes cannot be resubmitted. - Include both
entryIdandquizUserEntryIdwhen updating answer feedback — the API requires both for validation. - Use
scoreType=1(HIGHEST) for training scenarios where learners retake until passing. - Retrieve quiz config with
quiz.getto checkattemptsAllowedbefore starting new attempts. - Register cleanup before assertions in tests. Quiz cue points and user entries persist — always clean up test data.
20. Related Guides¶
- Cue Points Hub — Base cue point concepts, shared CRUD, eSearch integration
- Player Embed Guide — Player v7 setup, IVQ plugin configuration
- REACH API — AI-powered quiz generation (serviceFeature=12)
- Content Lab API — AI quiz generation widget
- Gamification API — Quiz scores as gamification inputs
- Analytics Reports API — Quiz engagement analytics