Developers
Two paths: embeds (no key needed, every plan) and the REST API (Institution/Premium plans).
1) Embeds — copy-paste, free
Paste into any website, WordPress, Google Sites or LMS. The "Embed" button on the host screen copies the code for you.
Join card (enter by PIN)
<iframe src="https://quizjhor.com/embed/join?pin=482013&lang=bn"
width="100%" height="380" style="border:0;border-radius:16px"
title="QuizJhor" loading="lazy"></iframe>Results list
<iframe src="https://quizjhor.com/embed/m/<token>?lang=bn"
width="100%" height="520" style="border:0;border-radius:16px"
title="QuizJhor results" loading="lazy"></iframe>Play inside the frame (new)
<iframe src="https://quizjhor.com/play?pin=482013&lang=bn"
width="100%" height="720" style="border:0;border-radius:16px"
title="QuizJhor" allow="autoplay; fullscreen" loading="lazy"></iframe>- • The join card opens the game in a new tab; the /play embed above runs the game inside the frame — give it a tall frame (640px+) and `allow="autoplay; fullscreen"`.
- • Some browsers (older Safari, lockdown mode) block storage for embedded pages — the page then shows a "Play in a new tab" button, so nobody gets stuck mid-game.
- • `lang=bn` or `lang=en` — viewers see the language you copied the snippet in.
- • If your LMS uses the `sandbox` attribute, include `allow-same-origin allow-scripts allow-popups` — without `allow-same-origin` the browser gives the page an opaque, storage-less origin and the game can't run at all.
- • The "Powered by QuizJhor" line is hidden on white-label plans.
2) REST API
The workspace owner creates a key from Dashboard → Developers. The key is shown only once.
curl -H "Authorization: Bearer qj_live_..." \
-H "X-API-Version: 1" \
https://quizjhor.com/api/v1/me| Endpoint | What it returns |
|---|---|
GET /api/v1/me | Verify key & workspace — proves every gate in one call |
GET /api/v1/quizzes | List quizzes (title, subject, question count) |
GET /api/v1/games | List sessions; `?since=` for incremental sync |
GET /api/v1/games/:id/results | Results for one session — name, score, rank, percentile |
POST /api/v1/games | Start a session (needs a write key) — returns PIN, joinUrl, embedUrl |
Rules
- •
?limit=— default 50, max 100. - • For a running session `results` is `null` — no results until the assessment finishes.
- • A key reads only its own workspace; another workspace's id returns 404.
- • Guardian phone numbers, roll numbers and certificate codes are never returned by the API.
- • Responses carry the server's API version; new fields may be added, existing field names won't change.
- • Keys are read-only by default. Tick "allow creating games" when creating the key to start sessions.
- • Creating a session enforces every rule the web app does — plan limits, the quiz having questions, no blank questions. If a session for that quiz is already running, you get it back instead of a new PIN.
Errors
- • 401 — Key missing, wrong, or revoked.
- • 403 — Key is valid but the plan lacks API access, the workspace is suspended, or the scope is missing.
- • 409 — On session create: the workspace has no host to attribute it to.
- • 429 — Too many requests — slow down.
3) Webhook — results pushed to you
Set your https URL in Dashboard → Developers. When an assessment finishes we POST to it — no polling.
POST <your-url>
X-QJ-Event: game.ended
X-QJ-Timestamp: 1730000000000
X-QJ-Signature: <hex>
{"event":"game.ended","deliveryId":"...","game":{...},"results":[...]}Verify the signature
const expected = crypto.createHmac("sha256", secret)
.update(timestamp + "." + rawBody).digest("hex");
// crypto.timingSafeEqual(expected, headerSignature)- • If we don't get a 2xx we retry up to 5 times, then mark it failed.
- • Each event is sent once per session — dedupe on deliveryId if you like.
- • Changing the URL issues a new secret; the old one stops working.