### Health GET http://localhost:8000/health ### Courses - list GET http://localhost:8000/api/courses?page=1&pageSize=20 ### Courses - detail GET http://localhost:8000/api/courses/c1 ### Courses - detail 404 GET http://localhost:8000/api/courses/unknown ### Modules - progress update POST http://localhost:8000/api/modules/m1/progress Content-Type: application/json { "progressPercent": 50 } ### Assignments - submission requires idempotencyKey (400) POST http://localhost:8000/api/assignments/a1/submission Content-Type: application/json { "content": "Answer" } ### Assignments - submission created (201) POST http://localhost:8000/api/assignments/a1/submission Content-Type: application/json { "content": "Answer", "idempotencyKey": "abc" } ### Assignments - duplicate idempotency (409) POST http://localhost:8000/api/assignments/a1/submission Content-Type: application/json { "content": "Answer", "idempotencyKey": "dup-key" } ### Exam Session - create requires fields (400) POST http://localhost:8000/api/exam-session Content-Type: application/json {} ### Exam Session - create (201) POST http://localhost:8000/api/exam-session Content-Type: application/json { "examId": "exam-001", "userId": "u1" } ### Exam Session - score answers (200) POST http://localhost:8000/api/exam-session/{{sessionId}}/score Content-Type: application/json Authorization: Bearer test-token { "idempotencyKey": "score-1", "mode": "batch", "answers": [ { "questionId": "mcq-1", "type": "enhanced_mcq", "choiceIndex": 2, "confidence": 4 }, { "questionId": "video-1", "type": "video_scenario", "scenarioAnswers": [ { "stepId": "s1", "selectedOptionId": "optA" }, { "stepId": "s2", "selectedOptionId": "optB" } ] }, { "questionId": "hotspot-1", "type": "image_hotspot", "selectedHotspots": ["spot1", "spot3"] }, { "questionId": "gallery-1", "type": "media_gallery", "viewedItems": ["itemA", "itemB", "itemC"] }, { "questionId": "puzzle-1", "type": "puzzle", "matches": [ { "pieceId": "p1", "targetId": "t1" }, { "pieceId": "p2", "targetId": "t2" } ] }, { "questionId": "scenario-1", "type": "scenario", "selectedOptionId": "optX" } ] } ### Exam Session - score MCQ only (200) POST http://localhost:8000/api/exam-session/{{sessionId}}/score Content-Type: application/json Authorization: Bearer test-token { "idempotencyKey": "score-mcq-1", "mode": "batch", "answers": [ { "questionId": "q1", "choiceIndex": 0 }, { "questionId": "q2", "choiceIndex": 2 } ] } ### Exam Session - duplicate idempotency (409) POST http://localhost:8000/api/exam-session/{{sessionId}}/score Content-Type: application/json { "idempotencyKey": "score-1", "answers": [] } ### Exam Session - summary GET http://localhost:8000/api/exam-session/{{sessionId}}/summary Authorization: Bearer test-token ### Exam Session - summary example response { "examId": "interactive-dapur-mbg-001", "examTitle": "Kuis Interaktif Dapur MBG", "totalQuestions": 10, "correctAnswers": 8, "score": 85, "timeSpent": "25 menit 30 detik", "completedAt": "2025-11-12T08:30:00Z", "answers": [ { "questionId": "1", "question": "Apa langkah pertama dalam memastikan keamanan pangan di Dapur MBG?", "userAnswer": "Melakukan analisis bahaya", "correctAnswer": "Melakukan analisis bahaya", "isCorrect": true, "type": "enhanced_mcq", "details": { "choiceIndex": 0, "confidence": 4 } }, { "questionId": "video-scenario-1", "question": "Analisis Situasi Dapur: Identifikasi Masalah Keamanan Pangan", "userAnswer": "step1: opt4; step2: opt4", "correctAnswer": "step1: opt4; step2: opt4", "isCorrect": true, "type": "video_scenario", "details": { "steps": [ { "stepId": "step1", "selectedOptionId": "opt4", "isCorrect": true, "earnedPoints": 10 }, { "stepId": "step2", "selectedOptionId": "opt4", "isCorrect": true, "earnedPoints": 10 } ] } }, { "questionId": "hotspot-scenario-1", "question": "Identifikasi Area Bermasalah dalam Tata Letak Dapur", "userAnswer": "hotspot1,hotspot2,hotspot4", "correctAnswer": "hotspot1,hotspot2,hotspot3,hotspot4,hotspot5", "isCorrect": false, "type": "image_hotspot", "details": { "selectedHotspots": ["hotspot1", "hotspot2", "hotspot4"], "correctHotspots": ["hotspot1", "hotspot2", "hotspot3", "hotspot4", "hotspot5"] } }, { "questionId": "3", "question": "Cocokkan istilah keamanan pangan dengan definisinya", "userAnswer": "ccp->def1; haccp->def2; sanitasi->def3; kontaminasi->def4", "correctAnswer": "ccp->def1; haccp->def2; sanitasi->def3; kontaminasi->def4", "isCorrect": true, "type": "puzzle", "details": { "matches": [ { "pieceId": "ccp", "targetId": "def1" }, { "pieceId": "haccp", "targetId": "def2" } ], "correctPairs": 4, "earnedPoints": 10 } } ] } ### Exams - list GET http://localhost:8000/api/exams?page=1&pageSize=20 ### Exams - filter by q GET http://localhost:8000/api/exams?page=1&pageSize=20&q=HACCP ### Certificates - resend requires fields (400) POST http://localhost:8000/api/certificates/abc/resend Content-Type: application/json { "idempotencyKey": "id1" } ### Certificates - resend accepted and throttled POST http://localhost:8000/api/certificates/abc/resend Content-Type: application/json { "recipientEmail": "x@y.com", "idempotencyKey": "cert-1" }