// ═══════════════════════════════════════════════════════════════════════════ // API REFERENCE PAGE - Full documentation for all public API endpoints // ═══════════════════════════════════════════════════════════════════════════ 'use client'; import { useState } from 'react'; const BASE = typeof window !== 'undefined' ? `${window.location.protocol}//${window.location.host}` : 'https://your-hf-space.hf.space'; // ─── colour tokens ─────────────────────────────────────────────────────────── const C = { bg: '#0d1117', surface: '#161b22', border: '#30363d', accent: '#58a6ff', green: '#3fb950', purple: '#bc8cff', orange: '#ffa657', red: '#f85149', text: '#e6edf3', muted: '#8b949e', get: '#3fb950', post: '#58a6ff', del: '#f85149', }; // ─── types ─────────────────────────────────────────────────────────────────── interface Param { name: string; type: string; required: boolean; desc: string; } interface Endpoint { method: 'GET' | 'POST' | 'DELETE'; path: string; summary: string; desc: string; params?: Param[]; body?: Param[]; example?: string; response?: string; tag: string; } const ENDPOINTS: Endpoint[] = [ // ── SESSION ────────────────────────────────────────────────────────────── { tag: 'Session', method: 'POST', path: '/api/session', summary: 'Create Session (Teacher)', desc: 'Creates a new teaching session. Returns a 4-digit room code students use to join.', body: [ { name: 'action', type: '"create"', required: true, desc: 'Must be "create"' }, { name: 'teacherId', type: 'string', required: true, desc: 'Unique teacher identifier' }, { name: 'teacherName', type: 'string', required: true, desc: 'Teacher display name' }, { name: 'sessionName', type: 'string', required: true, desc: 'Session / class name' }, { name: 'description', type: 'string', required: false, desc: 'Optional description' }, ], example: `fetch('/api/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', teacherId: 'teacher-001', teacherName: 'Mr. Sharma', sessionName: 'Physics – Chapter 5' }) })`, response: `{ "success": true, "data": { "id": "uuid", "roomCode": "4821", "isActive": true, ... } }`, }, { tag: 'Session', method: 'POST', path: '/api/session', summary: 'Lookup Session by Room Code', desc: 'Find a session using the 4-digit room code. Call this before joining.', body: [ { name: 'action', type: '"lookup"', required: true, desc: 'Must be "lookup"' }, { name: 'roomCode', type: 'string', required: true, desc: '4-digit room code' }, ], example: `fetch('/api/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'lookup', roomCode: '4821' }) })`, response: `{ "success": true, "data": { "id": "uuid", "roomCode": "4821", ... } }`, }, { tag: 'Session', method: 'POST', path: '/api/session', summary: 'Join Session (Student)', desc: 'Student joins a session. Returns current slide, active poll, and session state.', body: [ { name: 'action', type: '"join"', required: true, desc: 'Must be "join"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID (or 4-digit room code)' }, { name: 'userId', type: 'string', required: true, desc: 'Unique student ID' }, { name: 'userName', type: 'string', required: true, desc: 'Student display name' }, ], example: `fetch('/api/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'join', sessionId: '4821', // room code also accepted userId: 'student-xyz', userName: 'Rahul Kumar' }) })`, response: `{ "success": true, "data": { "user": {...}, "currentSlide": 0, "activePoll": null, ... } }`, }, { tag: 'Session', method: 'POST', path: '/api/session', summary: 'End Session (Teacher)', desc: 'Ends the session and broadcasts a session_ended SSE event to all connected students.', body: [ { name: 'action', type: '"end"', required: true, desc: 'Must be "end"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID to end' }, ], example: `fetch('/api/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'end', sessionId: 'uuid' }) })`, response: `{ "success": true, "message": "Session ended successfully" }`, }, { tag: 'Session', method: 'POST', path: '/api/session', summary: 'List All Active Sessions', desc: 'Returns all active sessions.', body: [{ name: 'action', type: '"list"', required: true, desc: 'Must be "list"' }], example: `fetch('/api/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'list' }) })`, response: `{ "success": true, "data": [ { "id": "...", "roomCode": "4821", ... } ] }`, }, // ── SSE EVENTS ─────────────────────────────────────────────────────────── { tag: 'Events (SSE)', method: 'GET', path: '/api/events', summary: 'Subscribe to Real-time Events', desc: `Opens a Server-Sent Events (SSE) stream. Both teachers and students connect here to receive live updates. **SSE Events emitted:** | Event | Sent when | |---|---| | \`connected\` | Stream opens (includes full session state) | | \`slide_change\` | Teacher changes slide (includes imageBase64) | | \`new_poll\` | Teacher creates a poll | | \`poll_update\` | A student votes | | \`poll_ended\` | Teacher ends poll | | \`correct_answer\` | Teacher reveals correct answer | | \`new_doubt\` | Student submits a doubt | | \`doubt_solved\` | Teacher resolves a doubt | | \`student_joined\` | A student joins | | \`session_ended\` | Teacher ends session |`, params: [ { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'userId', type: 'string', required: true, desc: 'Your user ID' }, ], example: `const url = '/api/events?sessionId=UUID&userId=STUDENT_ID'; const es = new EventSource(url); es.addEventListener('slide_change', e => { const { slideNumber, imageBase64 } = JSON.parse(e.data); imgEl.src = imageBase64; }); es.addEventListener('new_poll', e => { const poll = JSON.parse(e.data); showPoll(poll); }); es.addEventListener('correct_answer', e => { const { pollId, correctOptionIds } = JSON.parse(e.data); highlightAnswer(correctOptionIds); }); es.addEventListener('new_doubt', e => { // Teacher-side: student sent a doubt const doubt = JSON.parse(e.data); addDoubtCard(doubt); });`, response: `text/event-stream — raw SSE`, }, // ── SLIDE ──────────────────────────────────────────────────────────────── { tag: 'Slide', method: 'POST', path: '/api/slide', summary: 'Upload + Broadcast Slide (Teacher)', desc: 'Upload a base64 slide image. Automatically broadcasts slide_change SSE to all students with the image.', body: [ { name: 'action', type: '"upload"', required: true, desc: 'Must be "upload"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'slideImage', type: 'string', required: true, desc: 'Base64 image (data:image/png;base64,... or raw base64)' }, { name: 'slideNumber', type: 'number', required: false, desc: 'Slide index to replace (appends if omitted)' }, ], example: `// In Electron / SlideMap app: const base64 = canvas.toDataURL('image/png'); fetch('/api/slide', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'upload', sessionId: 'uuid', slideImage: base64, slideNumber: 3 // optional – omit to append }) })`, response: `{ "success": true, "data": { "slideNumber": 3, "totalSlides": 4 } }`, }, { tag: 'Slide', method: 'POST', path: '/api/slide', summary: 'Go to Slide (Teacher)', desc: 'Change the current slide without uploading a new image.', body: [ { name: 'action', type: '"goto"', required: true, desc: 'Must be "goto"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'slideNumber', type: 'number', required: true, desc: '0-indexed slide number' }, ], example: `fetch('/api/slide', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'goto', sessionId: 'uuid', slideNumber: 2 }) })`, response: `{ "success": true, "data": { "slideNumber": 2, "totalSlides": 5 } }`, }, // ── POLL ───────────────────────────────────────────────────────────────── { tag: 'Poll', method: 'POST', path: '/api/poll', summary: 'Create Poll (Teacher)', desc: 'Create a new poll. Any existing active poll is auto-ended. Broadcasts new_poll SSE to all students.', body: [ { name: 'action', type: '"create"', required: true, desc: 'Must be "create"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'question', type: 'string', required: true, desc: 'Poll question text' }, { name: 'options', type: 'string[]', required: true, desc: 'Array of option strings (min 2)' }, { name: 'type', type: '"single"|"multiple"', required: false, desc: 'Vote type (default: "single")' }, { name: 'correctAnswer', type: 'string', required: false, desc: 'Correct option text (for quizzes)' }, { name: 'duration', type: 'number', required: false, desc: 'Auto-end after N seconds (0 = manual)' }, ], example: `fetch('/api/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', sessionId: 'uuid', question: 'What is Newton\\'s 2nd Law?', options: ['F=ma', 'E=mc²', 'PV=nRT', 'V=IR'], correctAnswer: 'F=ma', duration: 0 // 0 = no timer }) })`, response: `{ "success": true, "data": { "id": "uuid", "question": "...", "options": [...], "isActive": true } }`, }, { tag: 'Poll', method: 'POST', path: '/api/poll', summary: 'Vote on Poll (Student)', desc: 'Student submits their answer. Each student can vote only once. Broadcasts poll_update SSE.', body: [ { name: 'action', type: '"vote"', required: true, desc: 'Must be "vote"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'pollId', type: 'string', required: true, desc: 'Poll ID' }, { name: 'option', type: 'string', required: true, desc: 'Option text the student selected' }, { name: 'studentId', type: 'string', required: true, desc: 'Student unique ID' }, ], example: `fetch('/api/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'vote', sessionId: 'uuid', pollId: 'poll-uuid', option: 'F=ma', studentId: 'student-xyz' }) })`, response: `{ "success": true, "data": { "pollId": "...", "results": {"F=ma":5,"E=mc²":2}, "totalVotes": 7 } }`, }, { tag: 'Poll', method: 'POST', path: '/api/poll', summary: 'End Poll (Teacher)', desc: 'Ends active poll and broadcasts poll_ended SSE with final results.', body: [ { name: 'action', type: '"end"', required: true, desc: 'Must be "end"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'pollId', type: 'string', required: true, desc: 'Poll ID to end' }, ], example: `fetch('/api/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'end', sessionId: 'uuid', pollId: 'poll-uuid' }) })`, response: `{ "success": true, "message": "Poll ended successfully" }`, }, { tag: 'Poll', method: 'POST', path: '/api/poll', summary: 'Reveal Correct Answer (Teacher)', desc: 'Broadcasts correct_answer SSE so all students see the highlighted answer.', body: [ { name: 'action', type: '"setCorrectAnswers"', required: true, desc: 'Must be "setCorrectAnswers"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'pollId', type: 'string', required: true, desc: 'Poll ID' }, { name: 'correctOptionIds', type: 'string[]', required: false, desc: 'Array of correct option id strings' }, { name: 'correctAnswer', type: 'string', required: false, desc: 'Correct option text' }, ], example: `fetch('/api/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'setCorrectAnswers', sessionId: 'uuid', pollId: 'poll-uuid', correctAnswer: 'F=ma' }) })`, response: `{ "success": true, "message": "Correct answers broadcast to students" }`, }, { tag: 'Poll', method: 'GET', path: '/api/poll', summary: 'Get All Polls (Teacher/Student)', desc: 'Returns all polls (active and ended) for a session.', params: [ { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, ], example: `fetch('/api/poll?sessionId=uuid')`, response: `{ "success": true, "data": { "polls": [...], "activePoll": {...}|null, "totalPolls": 3 } }`, }, // ── DOUBT ───────────────────────────────────────────────────────────────── { tag: 'Doubt', method: 'POST', path: '/api/doubt', summary: 'Submit Doubt (Student)', desc: 'Student submits a doubt/question. Broadcasts new_doubt SSE to the teacher.', body: [ { name: 'action', type: '"create"', required: true, desc: 'Must be "create"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'studentId', type: 'string', required: true, desc: 'Student unique ID' }, { name: 'studentName', type: 'string', required: true, desc: 'Student display name' }, { name: 'question', type: 'string', required: true, desc: 'The doubt text' }, { name: 'image', type: 'string', required: false, desc: 'Optional base64 image (data:image/...)' }, { name: 'slideNumber', type: 'number', required: false, desc: 'Slide where doubt occurred' }, ], example: `fetch('/api/doubt', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'create', sessionId: 'uuid', studentId: 'student-xyz', studentName: 'Rahul Kumar', question: 'I don\\'t understand why F=ma and not F=mv', slideNumber: 3 }) })`, response: `{ "success": true, "data": { "id": "uuid", "status": "pending", "question": "..." } }`, }, { tag: 'Doubt', method: 'POST', path: '/api/doubt', summary: 'Resolve Doubt (Teacher)', desc: 'Teacher marks doubt as solved. Broadcasts doubt_solved SSE to student.', body: [ { name: 'action', type: '"solve"', required: true, desc: 'Must be "solve"' }, { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'doubtId', type: 'string', required: true, desc: 'Doubt ID to resolve' }, { name: 'teacherResponse', type: 'string', required: false, desc: 'Text explanation from teacher' }, { name: 'annotatedImage', type: 'string', required: false, desc: 'Base64 annotated image' }, ], example: `fetch('/api/doubt', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'solve', sessionId: 'uuid', doubtId: 'doubt-uuid', teacherResponse: 'Because acceleration is the rate of change of velocity, not velocity itself.' }) })`, response: `{ "success": true, "data": { "id": "...", "status": "solved", "teacherResponse": "..." } }`, }, { tag: 'Doubt', method: 'GET', path: '/api/doubt', summary: 'Get All Doubts (Teacher)', desc: 'Returns all doubts grouped by status (pending / in_progress / solved).', params: [{ name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }], example: `fetch('/api/doubt?sessionId=uuid')`, response: `{ "success": true, "data": { "doubts": [...], "summary": { "pending": 2, "solved": 5 } } }`, }, // ── CHAT ───────────────────────────────────────────────────────────────── { tag: 'Chat', method: 'POST', path: '/api/chat', summary: 'Send Chat Message', desc: 'Send a chat message. Broadcasts chat_message SSE to all participants.', body: [ { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'userId', type: 'string', required: true, desc: 'Sender unique ID' }, { name: 'userName', type: 'string', required: true, desc: 'Sender display name' }, { name: 'message', type: 'string', required: true, desc: 'Message content' }, { name: 'isTeacher', type: 'boolean', required: false, desc: 'Flag if sender is teacher' }, ], example: `fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: 'uuid', userId: 'student-xyz', userName: 'Rahul', message: 'Can you explain again?', isTeacher: false }) })`, response: `{ "success": true, "data": { "id": "...", "message": "...", "timestamp": "..." } }`, }, { tag: 'Chat', method: 'GET', path: '/api/chat', summary: 'Get Chat History', desc: 'Returns the last N chat messages for a session.', params: [ { name: 'sessionId', type: 'string', required: true, desc: 'Session ID' }, { name: 'limit', type: 'number', required: false, desc: 'Max messages to return (default 100)' }, ], example: `fetch('/api/chat?sessionId=uuid&limit=50')`, response: `{ "success": true, "data": { "messages": [...], "count": 23 } }`, }, ]; const TAGS = ['Session', 'Events (SSE)', 'Slide', 'Poll', 'Doubt', 'Chat']; const METHOD_COLOR: Record = { GET: C.green, POST: C.accent, DELETE: C.red }; export default function ApiDocsPage() { const [activeTag, setActiveTag] = useState('Session'); const [openIdx, setOpenIdx] = useState(null); const [copied, setCopied] = useState(''); const filtered = ENDPOINTS.filter(e => e.tag === activeTag); const copy = (text: string, key: string) => { navigator.clipboard.writeText(text); setCopied(key); setTimeout(() => setCopied(''), 1500); }; return (
{/* ── Header ── */}

SlideMap API Reference

REST + SSE · CORS enabled · Any frontend can connect

{/* Base URL */}
BASE URL {BASE}
{/* Flow diagram */}
📡 How it works
{['Teacher creates session', '→', 'Students look up & join', '→', 'Both subscribe to /api/events (SSE)', '→', 'Teacher uploads slides → students see live', '→', 'Teacher creates polls → students vote', '→', 'Students submit doubts → teacher resolves'].map((t, i) => ( t === '→' ? : {t} ))}
{/* Tabs */}
{TAGS.map(tag => ( ))}
{/* ── Endpoints ── */}
{filtered.map((ep, idx) => { const isOpen = openIdx === idx; return (
{/* Header row */} {/* Body */} {isOpen && (
{/* Description */}

{ep.desc}

{/* Query params */} {ep.params && (
QUERY PARAMETERS
{['Name', 'Type', 'Required', 'Description'].map(h => ( ))} {ep.params.map((p, pi) => ( ))}
{h}
{p.name} {p.type} {p.required ? 'Yes' : 'No'} {p.desc}
)} {/* Request body */} {ep.body && (
REQUEST BODY (JSON)
{['Field', 'Type', 'Required', 'Description'].map(h => ( ))} {ep.body.map((p, pi) => ( ))}
{h}
{p.name} {p.type} {p.required ? 'Yes' : 'No'} {p.desc}
)} {/* Example */} {ep.example && (
EXAMPLE
{ep.example}
)} {/* Response */} {ep.response && (
RESPONSE
{ep.response}
)}
)}
); })}
); }