═══════════════════════════════════════════════════════════════════════════════ SLIDEMAP API DOCUMENTATION HF Space: https://Mafia2008-sar.hf.space API Docs UI: https://Mafia2008-sar.hf.space/api-docs CORS: Enabled (any frontend can call these APIs) Protocol: REST (JSON) + SSE (Server-Sent Events) ═══════════════════════════════════════════════════════════════════════════════ BASE URL ──────── https://Mafia2008-sar.hf.space HOW IT WORKS (Flow) ─────────────────── 1. Teacher creates a session → gets a 4-digit room code 2. Students look up session by room code → get sessionId 3. Both Teacher & Students subscribe to /api/events (SSE stream) 4. Teacher uploads slides → students see them live via SSE 5. Teacher creates polls → students vote via API 6. Students submit doubts → teacher resolves via API 7. Teacher ends session → all students notified via SSE ═══════════════════════════════════════════════════════════════════════════════ 1. SESSION API POST /api/session ═══════════════════════════════════════════════════════════════════════════════ ── 1.1 Create Session (Teacher) ───────────────────────────────────────────── POST /api/session Content-Type: application/json Request Body: { "action": "create", // Required "teacherId": "string", // Required - unique teacher ID "teacherName": "string", // Required - display name "sessionName": "string", // Required - class/session name "description": "string" // Optional } Response: { "success": true, "data": { "id": "uuid-string", "roomCode": "4821", // 4-digit code for students "name": "Physics Ch5", "teacherId": "teacher-001", "teacherName": "Mr. Sharma", "isActive": true, "currentSlide": 0, "totalSlides": 0, "studentCount": 0, "createdAt": "2026-04-14T16:00:00.000Z" } } Example (JS): fetch('https://Mafia2008-sar.hf.space/api/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', teacherId: 'teacher-001', teacherName: 'Mr. Sharma', sessionName: 'Physics – Chapter 5' }) }); ── 1.2 Lookup Session by Room Code ────────────────────────────────────────── POST /api/session Content-Type: application/json Request Body: { "action": "lookup", // Required "roomCode": "4821" // Required - 4-digit room code } Response: { "success": true, "data": { "id": "uuid", "roomCode": "4821", ... } } Example (JS): fetch('https://Mafia2008-sar.hf.space/api/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'lookup', roomCode: '4821' }) }); ── 1.3 Join Session (Student) ─────────────────────────────────────────────── POST /api/session Content-Type: application/json Request Body: { "action": "join", // Required "sessionId": "4821", // Required - sessionId OR room code "userId": "student-xyz", // Required - unique student ID "userName": "Rahul Kumar" // Required - display name } Response: { "success": true, "data": { "user": { "id": "...", "name": "Rahul Kumar", "role": "student" }, "session": { ... }, "currentSlide": 0, "activePoll": null, "whiteboardStrokes": [], "reactions": {} } } Example (JS): fetch('https://Mafia2008-sar.hf.space/api/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'join', sessionId: '4821', // room code accepted userId: 'student-xyz', userName: 'Rahul Kumar' }) }); ── 1.4 End Session (Teacher) ──────────────────────────────────────────────── POST /api/session Content-Type: application/json Request Body: { "action": "end", // Required "sessionId": "uuid" // Required } Response: { "success": true, "message": "Session ended successfully" } SSE broadcast: session_ended → all connected clients ── 1.5 List All Active Sessions ───────────────────────────────────────────── POST /api/session Content-Type: application/json Request Body: { "action": "list" } Response: { "success": true, "data": [ { "id": "...", "roomCode": "4821", ... } ] } ── 1.6 Get Session State ──────────────────────────────────────────────────── POST /api/session Content-Type: application/json Request Body: { "action": "get", // Required "sessionId": "uuid" // Required } Response: { "success": true, "data": { "session": { ... }, "currentSlide": 2, "activePoll": { ... } | null, "doubts": [ ... ], "recentChats": [ ... ], "whiteboardStrokes": [ ... ], "reactions": { "👍": 3, "❤️": 1 }, "onlineUsers": [ ... ] } } ═══════════════════════════════════════════════════════════════════════════════ 2. EVENTS API (SSE - Server-Sent Events) GET /api/events ═══════════════════════════════════════════════════════════════════════════════ Opens a persistent SSE stream. Connect immediately after joining a session. Both teacher AND students should connect to this endpoint. URL: GET /api/events?sessionId=UUID&userId=YOUR_USER_ID Query Parameters: sessionId string Required The session ID userId string Required Your unique user ID Response Headers: Content-Type: text/event-stream Cache-Control: no-cache Access-Control-Allow-Origin: * ── SSE Events Table ────────────────────────────────────────────────────── Event Name | Triggered When | Data Fields ──────────────────|────────────────────────────────────|──────────────────── connected | Stream opens | sessionId, userId, full session state slide_change | Teacher changes/uploads slide | slideNumber, imageBase64, totalSlides, timestamp new_poll | Teacher creates a poll | id, question, options[], isActive, ... poll_update | A student votes | pollId, results{}, totalVotes, timestamp poll_ended | Teacher ends poll | pollId, results{}, totalVotes, correctAnswer correct_answer | Teacher reveals correct answer | pollId, correctOptionIds[], correctAnswer new_doubt | Student submits a doubt | id, studentName, question, slideNumber, status doubt_solved | Teacher resolves a doubt | id, status, teacherResponse, annotatedImage doubt_updated | Doubt status changes | id, status student_joined | A new student joins | userId, userName, studentCount student_left | A student disconnects | userId, userName, studentCount session_ended | Teacher ends the session | sessionId, endedAt chat_message | Someone sends a chat message | id, userId, userName, message, isTeacher reaction | Someone reacts | reactions{}, lastReaction state_sync | Reconnect (sends full state again) | full session state Example (JS): const es = new EventSource( 'https://Mafia2008-sar.hf.space/api/events?sessionId=UUID&userId=STUDENT_ID' ); // Connected - get initial state es.addEventListener('connected', e => { const state = JSON.parse(e.data); console.log('Session state:', state); }); // Slide changed by teacher es.addEventListener('slide_change', e => { const { slideNumber, imageBase64, totalSlides } = JSON.parse(e.data); document.getElementById('slide-img').src = imageBase64; }); // New poll created es.addEventListener('new_poll', e => { const poll = JSON.parse(e.data); showPollUI(poll); }); // Live vote update es.addEventListener('poll_update', e => { const { pollId, results, totalVotes } = JSON.parse(e.data); updatePollResults(results); }); // Poll ended es.addEventListener('poll_ended', e => { const { pollId, results, correctAnswer } = JSON.parse(e.data); showFinalResults(results, correctAnswer); }); // Teacher reveals correct answer es.addEventListener('correct_answer', e => { const { pollId, correctOptionIds } = JSON.parse(e.data); highlightCorrectAnswer(correctOptionIds); }); // Student submitted a doubt (Teacher listens to this) es.addEventListener('new_doubt', e => { const doubt = JSON.parse(e.data); addDoubtCard(doubt); }); // Teacher resolved a doubt (Student listens to this) es.addEventListener('doubt_solved', e => { const doubt = JSON.parse(e.data); updateDoubtStatus(doubt); }); // Session ended es.addEventListener('session_ended', e => { alert('Session has ended'); es.close(); }); // Heartbeat (keep-alive, every 15s) - no action needed es.onerror = err => { console.error('SSE error:', err); // Reconnect logic here }; ═══════════════════════════════════════════════════════════════════════════════ 3. SLIDE API POST /api/slide ═══════════════════════════════════════════════════════════════════════════════ ── 3.1 Upload & Broadcast Slide (Teacher → Students) ──────────────────────── POST /api/slide Content-Type: application/json Request Body: { "action": "upload", // Required "sessionId": "uuid", // Required "slideImage": "data:image/png;base64,iVBOR...", // Required - base64 image "slideNumber": 3 // Optional - replaces slide at index; omit to append } Notes: - slideImage can be a data URL ("data:image/png;base64,...") or raw base64 - Automatically broadcasts slide_change SSE to ALL connected students - Students receive the imageBase64 directly — no separate fetch needed Response: { "success": true, "data": { "slideNumber": 3, "totalSlides": 4 } } Example (Electron / SlideMap app): const canvas = document.getElementById('myCanvas'); const base64 = canvas.toDataURL('image/png'); fetch('https://Mafia2008-sar.hf.space/api/slide', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'upload', sessionId: SESSION_ID, slideImage: base64, slideNumber: currentSlideIndex // optional }) }); ── 3.2 Go to Slide (Teacher) ──────────────────────────────────────────────── POST /api/slide Content-Type: application/json Request Body: { "action": "goto", // Required "sessionId": "uuid", // Required "slideNumber": 2 // Required - 0-indexed slide number } Response: { "success": true, "data": { "slideNumber": 2, "totalSlides": 5 } } ── 3.3 Next Slide ─────────────────────────────────────────────────────────── POST /api/slide Body: { "action": "next", "sessionId": "uuid" } Response: { "success": true, "data": { "slideNumber": 3, "totalSlides": 5 } } ── 3.4 Previous Slide ─────────────────────────────────────────────────────── POST /api/slide Body: { "action": "prev", "sessionId": "uuid" } Response: { "success": true, "data": { "slideNumber": 1, "totalSlides": 5 } } ═══════════════════════════════════════════════════════════════════════════════ 4. POLL API POST /api/poll (create/vote/end/setCorrectAnswers) GET /api/poll (fetch polls) ═══════════════════════════════════════════════════════════════════════════════ ── 4.1 Create Poll (Teacher) ──────────────────────────────────────────────── POST /api/poll Content-Type: application/json Request Body: { "action": "create", // Required "sessionId": "uuid", // Required "question": "string", // Required "options": ["A","B","C"], // Required - min 2 items "type": "single", // Optional - "single"|"multiple" (default: "single") "correctAnswer": "A", // Optional - marks correct option "duration": 0 // Optional - auto-end after N seconds (0 = manual) } Notes: - Any existing active poll is auto-ended before creating a new one - Broadcasts new_poll SSE to ALL students immediately Response: { "success": true, "data": { "id": "poll-uuid", "sessionId": "uuid", "question": "What is Newton's 2nd Law?", "options": [ { "id": "option_0", "text": "F=ma", "isCorrect": true, "votes": 0 }, { "id": "option_1", "text": "E=mc²", "isCorrect": false, "votes": 0 } ], "type": "single", "isActive": true, "results": { "F=ma": 0, "E=mc²": 0 }, "totalVotes": 0, "votedStudents": [], "createdAt": "2026-04-14T16:00:00.000Z" } } Example (JS): fetch('https://Mafia2008-sar.hf.space/api/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', sessionId: SESSION_ID, question: "What is Newton's 2nd Law?", options: ['F=ma', 'E=mc²', 'PV=nRT', 'V=IR'], correctAnswer: 'F=ma', duration: 0 }) }); ── 4.2 Vote on Poll (Student) ─────────────────────────────────────────────── POST /api/poll Content-Type: application/json Request Body: { "action": "vote", // Required "sessionId": "uuid", // Required "pollId": "poll-uuid", // Required "option": "F=ma", // Required - the option text student selected "studentId": "student-xyz" // Required - student unique ID } Notes: - Each student can vote only ONCE per poll - Broadcasts poll_update SSE to ALL clients with live vote counts Response: { "success": true, "data": { "pollId": "poll-uuid", "results": { "F=ma": 5, "E=mc²": 2 }, "totalVotes": 7, "voted": true } } Example (JS): fetch('https://Mafia2008-sar.hf.space/api/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'vote', sessionId: SESSION_ID, pollId: POLL_ID, option: 'F=ma', studentId: MY_STUDENT_ID }) }); ── 4.3 End Poll (Teacher) ─────────────────────────────────────────────────── POST /api/poll Content-Type: application/json Request Body: { "action": "end", // Required "sessionId": "uuid", // Required "pollId": "poll-uuid" // Required } Notes: - Broadcasts poll_ended SSE with final results + correctAnswer Response: { "success": true, "message": "Poll ended successfully" } ── 4.4 Reveal Correct Answer (Teacher) ────────────────────────────────────── POST /api/poll Content-Type: application/json Request Body: { "action": "setCorrectAnswers", // Required "sessionId": "uuid", // Required "pollId": "poll-uuid", // Required "correctAnswer": "F=ma", // Optional - option text "correctOptionIds": ["option_0"] // Optional - option id array } Notes: - Broadcasts correct_answer SSE so students can highlight the correct option - Use this AFTER ending the poll to reveal the answer Response: { "success": true, "message": "Correct answers broadcast to students" } ── 4.5 Get All Polls ──────────────────────────────────────────────────────── GET /api/poll?sessionId=UUID Response: { "success": true, "data": { "polls": [ { ... }, { ... } ], "activePoll": { ... } | null, "totalPolls": 3 } } ═══════════════════════════════════════════════════════════════════════════════ 5. DOUBT API POST /api/doubt (submit/resolve/update) GET /api/doubt (fetch doubts) ═══════════════════════════════════════════════════════════════════════════════ ── 5.1 Submit Doubt (Student → Teacher) ───────────────────────────────────── POST /api/doubt Content-Type: application/json Request Body: { "action": "create", // Required "sessionId": "uuid", // Required "studentId": "student-xyz", // Required "studentName": "Rahul Kumar", // Required "question": "string", // Required - the doubt text "image": "data:image/png;base64,...", // Optional "slideNumber": 3 // Optional - slide where doubt occurred } Notes: - Broadcasts new_doubt SSE to the teacher (and all connected clients) - slideNumber auto-filled from current slide if not provided Response: { "success": true, "data": { "id": "doubt-uuid", "sessionId": "uuid", "studentId": "student-xyz", "studentName": "Rahul Kumar", "question": "Why F=ma and not F=mv?", "slideNumber": 3, "status": "pending", "createdAt": "2026-04-14T16:00:00.000Z" } } Example (JS): fetch('https://Mafia2008-sar.hf.space/api/doubt', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', sessionId: SESSION_ID, studentId: MY_STUDENT_ID, studentName: MY_NAME, question: "I don't understand why F=ma and not F=mv", slideNumber: 3 }) }); ── 5.2 Resolve Doubt (Teacher) ────────────────────────────────────────────── POST /api/doubt Content-Type: application/json Request Body: { "action": "solve", // Required "sessionId": "uuid", // Required "doubtId": "doubt-uuid", // Required "teacherResponse": "string", // Optional - text explanation "annotatedImage": "data:image/..." // Optional - annotated image } Notes: - Broadcasts doubt_solved SSE so the student sees the resolution Response: { "success": true, "data": { "id": "doubt-uuid", "status": "solved", "teacherResponse": "Because acceleration = rate of change of velocity...", "solvedAt": "2026-04-14T16:05:00.000Z" } } ── 5.3 Update Doubt Status (Teacher) ──────────────────────────────────────── POST /api/doubt Content-Type: application/json Request Body: { "action": "update", // Required "sessionId": "uuid", // Required "doubtId": "doubt-uuid", // Required "status": "in_progress" // Required: "pending"|"in_progress"|"solved" } Notes: - Broadcasts doubt_updated SSE to all clients Response: { "success": true, "data": { "id": "...", "status": "in_progress" } } ── 5.4 Get All Doubts (Teacher) ───────────────────────────────────────────── GET /api/doubt?sessionId=UUID Response: { "success": true, "data": { "doubts": [ { ... }, { ... } ], "summary": { "total": 7, "pending": 2, "inProgress": 1, "solved": 4 } } } ═══════════════════════════════════════════════════════════════════════════════ 6. CHAT API POST /api/chat (send message) GET /api/chat (get history) ═══════════════════════════════════════════════════════════════════════════════ ── 6.1 Send Chat Message ──────────────────────────────────────────────────── POST /api/chat Content-Type: application/json Request Body: { "sessionId": "uuid", // Required "userId": "user-xyz", // Required "userName": "Rahul", // Required "message": "Can you explain again?", // Required "isTeacher": false // Optional (default: false) } Notes: - Broadcasts chat_message SSE to ALL participants Response: { "success": true, "data": { "id": "msg-uuid", "userId": "user-xyz", "userName": "Rahul", "message": "Can you explain again?", "isTeacher": false, "timestamp": "2026-04-14T16:10:00.000Z" } } ── 6.2 Get Chat History ───────────────────────────────────────────────────── GET /api/chat?sessionId=UUID&limit=100 Query Parameters: sessionId string Required Session ID limit number Optional Max messages to return (default: 100, max stored: 500) Response: { "success": true, "data": { "messages": [ { "id": "...", "message": "...", ... } ], "count": 23 } } ═══════════════════════════════════════════════════════════════════════════════ ERROR RESPONSES ═══════════════════════════════════════════════════════════════════════════════ All error responses follow this format: { "success": false, "error": "Descriptive error message" } HTTP Status Codes: 400 Bad Request - Missing or invalid fields 404 Not Found - Session/Poll/Doubt not found 500 Internal Error - Server error ═══════════════════════════════════════════════════════════════════════════════ COMPLETE INTEGRATION EXAMPLE (Student Frontend - Vanilla JS) ═══════════════════════════════════════════════════════════════════════════════ const BASE = 'https://Mafia2008-sar.hf.space'; let sessionId = null; let myUserId = 'student-' + Math.random().toString(36).substr(2, 9); let myName = 'Rahul Kumar'; // STEP 1: Look up session by room code async function joinByRoomCode(roomCode) { // Lookup const lookup = await fetch(`${BASE}/api/session`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'lookup', roomCode }) }).then(r => r.json()); if (!lookup.success) return alert('Room not found'); sessionId = lookup.data.id; // Join await fetch(`${BASE}/api/session`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'join', sessionId, userId: myUserId, userName: myName }) }); // STEP 2: Subscribe to SSE connectSSE(); } // STEP 2: SSE Connection function connectSSE() { const es = new EventSource(`${BASE}/api/events?sessionId=${sessionId}&userId=${myUserId}`); es.addEventListener('slide_change', e => { const { imageBase64 } = JSON.parse(e.data); document.getElementById('slide').src = imageBase64; }); es.addEventListener('new_poll', e => { const poll = JSON.parse(e.data); showPoll(poll); }); es.addEventListener('correct_answer', e => { const { correctOptionIds } = JSON.parse(e.data); highlightAnswer(correctOptionIds); }); es.addEventListener('doubt_solved', e => { const doubt = JSON.parse(e.data); showDoubtResolution(doubt); }); es.addEventListener('session_ended', () => { alert('Class ended'); es.close(); }); } // STEP 3: Vote on poll async function vote(pollId, option) { await fetch(`${BASE}/api/poll`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'vote', sessionId, pollId, option, studentId: myUserId }) }); } // STEP 4: Submit doubt async function submitDoubt(text) { await fetch(`${BASE}/api/doubt`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', sessionId, studentId: myUserId, studentName: myName, question: text }) }); } ═══════════════════════════════════════════════════════════════════════════════ COMPLETE INTEGRATION EXAMPLE (Teacher - Electron/SlideMap App) ═══════════════════════════════════════════════════════════════════════════════ const BASE = 'https://Mafia2008-sar.hf.space'; const teacherId = 'teacher-001'; const teacherName = 'Mr. Sharma'; let sessionId = null; // STEP 1: Create session async function createSession(sessionName) { const res = await fetch(`${BASE}/api/session`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', teacherId, teacherName, sessionName }) }).then(r => r.json()); sessionId = res.data.id; const roomCode = res.data.roomCode; // Show this to students console.log('Room Code:', roomCode); // Connect SSE to receive doubts + student events connectSSE(); } // STEP 2: Connect to SSE (teacher also connects to receive doubts) function connectSSE() { const es = new EventSource(`${BASE}/api/events?sessionId=${sessionId}&userId=${teacherId}`); es.addEventListener('new_doubt', e => { const doubt = JSON.parse(e.data); showDoubtInPanel(doubt); // Teacher sees student doubt }); es.addEventListener('student_joined', e => { const { userName, studentCount } = JSON.parse(e.data); console.log(`${userName} joined. Total: ${studentCount}`); }); } // STEP 3: Send slide when screen changes async function sendSlide(base64Image, slideIndex) { await fetch(`${BASE}/api/slide`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'upload', sessionId, slideImage: base64Image, slideNumber: slideIndex }) }); } // STEP 4: Create poll async function createPoll(question, options, correctAnswer) { const res = await fetch(`${BASE}/api/poll`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', sessionId, question, options, correctAnswer, duration: 0 }) }).then(r => r.json()); return res.data.id; // pollId } // STEP 5: End poll and reveal answer async function endPoll(pollId, correctAnswer) { await fetch(`${BASE}/api/poll`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'end', sessionId, pollId }) }); await fetch(`${BASE}/api/poll`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'setCorrectAnswers', sessionId, pollId, correctAnswer }) }); } // STEP 6: Resolve a doubt async function resolveDoubt(doubtId, response) { await fetch(`${BASE}/api/doubt`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'solve', sessionId, doubtId, teacherResponse: response }) }); } // STEP 7: End session async function endSession() { await fetch(`${BASE}/api/session`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'end', sessionId }) }); } ═══════════════════════════════════════════════════════════════════════════════ QUICK REFERENCE CHEATSHEET ═══════════════════════════════════════════════════════════════════════════════ Action Method Endpoint action field ───────────────────────────────────────────────────────────────────────── Create session (T) POST /api/session "create" Lookup by room code (S) POST /api/session "lookup" Join session (S) POST /api/session "join" End session (T) POST /api/session "end" List all sessions POST /api/session "list" ───────────────────────────────────────────────────────────────────────── Subscribe to live events GET /api/events (SSE stream) ───────────────────────────────────────────────────────────────────────── Upload + broadcast slide POST /api/slide "upload" Go to slide number (T) POST /api/slide "goto" Next slide (T) POST /api/slide "next" Prev slide (T) POST /api/slide "prev" ───────────────────────────────────────────────────────────────────────── Create poll (T) POST /api/poll "create" Vote on poll (S) POST /api/poll "vote" End poll (T) POST /api/poll "end" Reveal answer (T) POST /api/poll "setCorrectAnswers" Get all polls GET /api/poll ?sessionId=UUID ───────────────────────────────────────────────────────────────────────── Submit doubt (S) POST /api/doubt "create" Resolve doubt (T) POST /api/doubt "solve" Update doubt status (T) POST /api/doubt "update" Get all doubts (T) GET /api/doubt ?sessionId=UUID ───────────────────────────────────────────────────────────────────────── Send chat message POST /api/chat (no action field) Get chat history GET /api/chat ?sessionId=UUID&limit=N ───────────────────────────────────────────────────────────────────────── (T) = Teacher action (S) = Student action ═══════════════════════════════════════════════════════════════════════════════ END OF DOCUMENTATION Interactive UI: https://Mafia2008-sar.hf.space/api-docs ═══════════════════════════════════════════════════════════════════════════════