Skip to main content
View as Markdown

Video Quiz - Kaltura Player

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 disableentitlement for creating and managing quizzes. Viewer interactions (starting attempts, submitting answers) use a USER KS (type=0) scoped to the entry.
  • Cue Points plugin: The cuePoint server plugin must be enabled on the account (enabled by default on most accounts).
  • Quiz plugin: The quiz server plugin must be enabled on the account.
  • Player IVQ plugin: To render quizzes during playback, enable the ivq and kalturaCuepoints plugins in the player configuration.
  • Session management: See Session Guide for KS generation and privilege scoping.

3. Quiz Lifecycle​

media.add (create video/audio entry)
→ media.addContent (upload or import content)
→ quiz.add (mark entry as quiz — sets capabilities='quiz.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)

A quiz is built on top of a video or audio entry (mediaType=1 or mediaType=5). Create the source entry first, then call quiz.add to mark it as a quiz. This sets capabilities = 'quiz.quiz' on the entry, which is how the Player IVQ plugin and Content Hubs recognize it as a quiz entry. 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
Prerequisite: A video or audio entry (mediaType=1 or 5) must already exist. Create the entry with media.add + media.addContent first. See Upload & Ingestion.

curl -X POST "$KALTURA_SERVICE_URL/service/quiz_quiz/action/add" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "entryId=$KALTURA_ENTRY_ID" \
-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"

This action sets capabilities = 'quiz.quiz' on the entry automatically. The Player IVQ plugin and Content Hubs use this field to identify quiz entries and render quiz UI.

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)​

FieldTypeDescription
versionintAuto-incremented on update (readonly)
showResultOnAnswerintShow correct/incorrect immediately (-1/0/1)
showCorrectKeyOnAnswerintShow correct answer key while answering
allowAnswerUpdateintAllow changing answers before submission
showCorrectAfterSubmissionintReveal correct answers after submit
allowDownloadintAllow PDF download of quiz
showGradeAfterSubmissionintShow score after submit
attemptsAllowedintNumber of retakes allowed
scoreTypeintHow to calculate final score across attempts

6. Quiz Service Actions​

ActionDescription
addMark entry as quiz with configuration (sets capabilities='quiz.quiz')
getGet quiz config by entryId
updateUpdate quiz settings (increments version)
listList quiz entries (filter by entryIdEqual or entryIdIn)
serveDownload quiz as PDF
getUrlGet PDF download URL (quizOutputType: 1=PDF)

Discovering quiz entries: Use quiz.list with filter[entryIdEqual] or filter[entryIdIn] to retrieve quiz configurations. The capabilities field on entries ('quiz.quiz') is set automatically by quiz.add and is how the system identifies quiz entries.

Deleting quiz attempts: Use userEntry.delete with the user entry ID to remove a quiz attempt.

7. Question Types​

ValueNameDescription
1MULTIPLE_CHOICE_ANSWERSingle correct answer from options
2TRUE_FALSETrue/false question
3REFLECTION_POINTPause for reflection (no correct answer, not scored)
4MULTIPLE_ANSWER_QUESTIONMultiple correct answers
5FILL_IN_BLANKFill in the blank
6HOT_SPOTHotspot on video frame
7GO_TONavigation/branching point
8OPEN_QUESTIONFree-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]=$KALTURA_ENTRY_ID" \
-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]=$KALTURA_ENTRY_ID" \
-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]=$KALTURA_ENTRY_ID" \
-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]=$KALTURA_ENTRY_ID" \
-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]=$KALTURA_ENTRY_ID" \
-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​

FieldTypeDescription
questionstringQuestion text
questionTypeintQuestion type (see table above)
optionalAnswersarrayArray of answer choices
hintstringHint text shown to viewer
explanationstringExplanation (hidden from non-editors)
presentationOrderintDisplay order
excludeFromScoreintExclude 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]=$KALTURA_ENTRY_ID"

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]=$KALTURA_ENTRY_ID" \
-d "cuePoint[parentId]=$QUESTION_CUE_POINT_ID" \
-d "cuePoint[quizUserEntryId]=$QUIZ_USER_ENTRY_ID" \
-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]=$KALTURA_ENTRY_ID" \
-d "cuePoint[parentId]=$QUESTION_CUE_POINT_ID" \
-d "cuePoint[quizUserEntryId]=$QUIZ_USER_ENTRY_ID" \
-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=$QUIZ_USER_ENTRY_ID"

Returns the KalturaQuizUserEntry with score, calculatedScore, and status=quiz.3 (SUBMITTED).

10. Answer Fields​

FieldTypeDescription
parentIdstringQuestion cue point ID (insert-only, required)
quizUserEntryIdstringUser entry ID for this attempt (insert-only, required)
answerKeystringSelected answer key
openAnswerstringFree-text answer (max 1024 chars, for open questions)
isCorrectintServer-computed correctness (readonly)
correctAnswerKeysarrayCorrect answer keys from the question (readonly)
explanationstringExplanation copied from question (readonly)
feedbackstringInstructor 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​

FieldTypeDescription
scorefloatRaw quiz score (readonly)
calculatedScorefloatScore based on scoreType across attempts (readonly)
feedbackstringOverall instructor feedback (max 1024 chars)
versionintAttempt number, 0-based (readonly)
statusstring1=ACTIVE, 2=DELETED, quiz.3=SUBMITTED

12. Scoring Models​

ValueNameDescription
1HIGHESTBest score across all attempts
2LOWESTWorst score across all attempts
3LATESTMost recent attempt
4FIRSTFirst attempt only
5AVERAGEAverage across all attempts

13. Quiz Reports​

Four report types accessible via report.getTable:

reportTypeDescription
quiz.QUIZPer-question correct/wrong percentage
quiz.QUIZ_USER_PERCENTAGEPer-user overall percentage
quiz.QUIZ_AGGREGATE_BY_QUESTIONAggregated by specific question IDs
quiz.QUIZ_USER_AGGREGATE_BY_QUESTIONPer-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]=$KALTURA_ENTRY_ID" \
-d "reportInputFilter[timeZoneOffset]=0" \
-d "objectIds=$KALTURA_ENTRY_ID" \
-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:

plugins: {
kalturaCuepoints: {},
ivq: {}
}

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 QUIZ task (serviceFeature=12) via entryVendorTask.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.

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 the 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​

ErrorCauseFix
PROVIDED_ENTRY_IS_ALREADY_A_QUIZCalling quiz.add on entry with existing quiz configUse quiz.update to modify existing config
PARENT_CUE_POINT_NOT_FOUNDInvalid parentId on answer cue pointParent question must exist on the same entry
CANNOT_APPROVE_TASK-like for quizSubmitting already-submitted quizCheck userEntry.status before submission
PROPERTY_VALIDATION_CANNOT_BE_NULLAnswer without parentId or quizUserEntryIdBoth fields are required on answer creation
USER_ENTRY_NOT_FOUNDInvalid quizUserEntryId on answerStart an attempt with userEntry.add first

19. Best Practices​

  • Quiz answer security is server-enforced. Non-editors cannot see isCorrect or explanation on question cue points — no client-side hiding needed.
  • Use excludeFromScore=1 for reflection points (type=3) and any question not meant for grading.
  • Check userEntry.status before submission. Already-submitted quizzes cannot be resubmitted.
  • Include both entryId and quizUserEntryId when 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.get to check attemptsAllowed before starting new attempts.
  • Register cleanup before assertions in tests. Quiz cue points and user entries persist — always clean up test data.