Integration guide: building a mock-exam flow
This walks through a complete, production-shaped integration: let a student pick an exam, sit a timed past paper, and see a scored result — backed by verified MirrorMingo content.
We use the TypeScript SDK; the shape is identical in every language.
Architecture
Browser / app ──► Your backend ──► MirrorMingo Exam API
(holds the API key)
Keep the API key on your server. The browser talks to your backend; your backend talks to MirrorMingo. This keeps the key secret and lets you add your own auth, caching and analytics.
MirrorMingo School Intelligence is not part of this public API. It remains a school-subscription surface inside the MirrorMingo school dashboard.
1. Let the student choose an exam
// GET /api/exams (your backend)
app.get("/api/exams", async (req, res) => {
const exams = await mm.exams.list({ country: req.query.country as string });
res.json(exams);
});
Exam metadata (names, sections, scoring) changes rarely — cache it for a day.
2. List verified past papers
app.get("/api/exams/:examKey/papers", async (req, res) => {
const catalog = await mm.mockExams.listPapers(req.params.examKey);
res.json(catalog);
});
Each paper has a stable paper_id grouped under a year. Render a year picker.
3. Start a timed attempt
app.post("/api/exams/:examKey/start", async (req, res) => {
const attempt = await mm.mockExams.open(req.params.examKey, {
paperId: req.body.paperId,
questionLimit: 20,
learnerId: req.user.id,
});
// Persist attempt.attempt_id against the signed-in student so a refresh
// resumes the same attempt. Start your countdown from the exam's duration.
res.json(attempt);
});
For long papers, page with mm.mockExams.questions(examKey, { paperId, questionOffset, learnerId }).
has_visual: true marks questions that reference a figure — render a note for those.
4. Submit and score
app.post("/api/exams/:examKey/submit", async (req, res) => {
const result = await mm.mockExams.submit(req.params.examKey, {
attemptId: req.body.attemptId,
answers: req.body.answers, // { [question.answer_id]: "C", ... }
status: req.body.timedOut ? "expired" : "submitted",
learnerId: req.user.id,
});
res.json(result.feedback);
});
feedback carries the score and per-question correctness/explanations.
5. (Optional) React to scoring with webhooks
Instead of blocking the UI on scoring, subscribe to mock_attempt.scored and
update the student's dashboard when the event arrives — see
Webhooks.
Production checklist
- API key stored in a secret manager, never shipped to the client
- Exam/paper metadata cached (≥1 day)
-
attempt_idpersisted per student so attempts survive a refresh -
learnerIdpassed consistently for open, page, submit and history calls - Server-side countdown; submit with
status: "expired"on timeout - Handle
429with backoff (the SDK does this automatically) - Sandbox used in CI; production key only in prod