Exam Hall
We draw the screen. You mount one component and the learner sees the whole paper, navigates it, submits it, and gets graded feedback.Ships in a day. Difficulty 3/10.
Arcade
You draw the screen. We hand you one question at a time and grade it on demand. Hearts, streaks, timers, pacing, all yours.Ships in a week. Difficulty 6/10.
Which one
Answer one question honestly:Is the shape of the learner experience part of your product, or just the container for someone else’s content?If a learner would describe your practice mode as a feature of your app (“the one with the streaks”), that is Arcade. If they would describe it as “the test”, that is Exam Hall.
Start with Exam Hall even if Arcade is your end goal. It proves the token flow, the origin
allow-list and the webhook, which are the parts most likely to surprise you. Arcade then reuses
all three unchanged.
Before either mode
Three things, once. This is the part you cannot do alone, so start it first.1
Create your organisation and key
Sign up at edpire.com, create your organisation, then open
Integrations and create an API key.On the same screen, add every origin your player will be embedded on, including staging
and local development:Mobile apps in a WebView send no origin, or an opaque one like
capacitor://localhost.
Both are handled. You do not need to register anything for native.2
Install, or do not
On React 18 or 19, install the package:On anything else, skip this step entirely. Angular, Vue, Svelte, Rails, Django, plain
HTML, or React 15, 16 and 17 all use a single script tag instead, with no npm and no build
step. See Not on React below. The rest of this guide applies unchanged,
only the mounting syntax differs.Styles are bundled and scoped, so the SDK cannot leak CSS into your app and your Tailwind
version does not matter.
3
Store one ID per item
Add a nullable
edpire_assessment_id column to whatever table holds your lessons, tests
or exercises. That UUID is the entire contract between your catalogue and ours.Your content team authors in Edpire. List assessments by title in your own admin and store
the ID against your lesson. See Find an assessment ID, and
catalogue sync for keeping the list current.Exam Hall
The learner opens your page and sees the complete assessment: every question, a progress indicator, reading passages in a side panel where the assessment has them, and a submit button. When they submit, it grades and shows per question feedback without a page load.Step 1. Mint tokens on your server
Your API key never touches the browser. Instead your backend issues a short lived token scoped to one learner and one assessment.app/api/edpire/token/route.ts
toNodeHandler().
Step 2. Mount the player
Step 3. Give the learner somewhere to go
The player grades in place and then stops. Without a next step the learner is stranded on a finished exam. Two options, and you should use both:- Your own result screen, navigated to from
onComplete. This is the primary path, because the useful next action belongs to you: retry, next lesson, back to the class. returnUrlas a fallback, so the built in report button also lands somewhere sensible.
Step 4. Record the result
See Results, for both modes below. Done. That is Exam Hall in full.Arcade
The learner sees one question, answers it, gets told immediately whether they were right, and moves on. You own everything around the question: the progress bar, the lives, the streak counter, the celebration animation, the pacing. Edpire gives you two things here. A renderer that turns question content into a working interactive question, and a grader you can call per question.Step 1. Fetch the content on your server
Step 2. Flatten it into steps
An assessment nests questions inside exercises. Arcade wants a flat list.Import pure helpers from
@edpire/sdk/core, not @edpire/sdk. The main entry pulls in React
and the full player tree, which a server file cannot import. /core is the same functions with
none of that.Step 3. Render one question
EdpireQuestion is deliberately unstyled beyond what makes the interaction work. It inherits
your fonts and colours, and your CSS reaches into it on purpose. Style it to match your product.
Step 4. Grade the question
feedback straight back into EdpireQuestion and the right and wrong states appear on the
correct blanks, choices and pairs. You do not have to interpret it.
Proxy this through your own backend rather than calling Edpire from the browser, and validate
that the assessment is one you actually publish before forwarding. Otherwise any signed in user
can grade against any assessment ID they can guess.
Step 5. Submit the attempt
Per question checks do not create a submission. When the learner finishes, send the whole attempt once so it lands in reporting and fires your webhook.Step 6. Record the result
Same as Exam Hall. Read on.Results, for both modes
A result reaches you two ways, and you want both.1
Webhook, the primary path
Register an endpoint and we POST to it when a submission completes. Verify the signature with
EDPIRE_WEBHOOK_SECRET. See Webhooks.Handle it idempotently. Retries are real, and a delivery can arrive twice.2
Client reconciliation, the safety net
onComplete hands you the submission ID the moment grading finishes. Take it, re-fetch the
submission server to server, confirm the learner_ref matches the session, and write the
score.This is what saves you when a webhook is delayed or lost. Without it a dropped delivery leaves
the attempt stuck as pending forever, and the learner sees a blank result screen.is_fully_graded and wait for submission.grading.completed before showing a score as final.
Not on React
Both modes work without React, without npm and without a build step. The CDN bundle carries its own React inside a closure, so your framework and your React version are irrelevant to it. It does not touchwindow.React, so it cannot collide with a React app that already exists on the
page.
mount() returns an object with unmount(), which is what you call from your framework’s
teardown hook: ngOnDestroy, onUnmounted, onDestroy, or a router leave guard.
For Arcade without React, EdpireSDK.renderQuestion() is the imperative equivalent of
<EdpireQuestion>. It returns an instance with setContent(), setFeedback() and unmount(),
so you drive it from your own loop:
EdpireSDK.flattenAssessment and EdpireSDK.buildSubmitPayload are on the same global.
React version support
How hard is this, honestly
Exam Hall: 3 out of 10. One server endpoint you can copy verbatim, one component, one webhook handler. A competent React developer does it in an afternoon. The only genuinely fiddly parts are remembering to size the container and getting your origins registered before you test. Arcade: 6 out of 10. The Edpire specific parts are still easy.flattenAssessment, EdpireQuestion and /check are
maybe a day. The other four to seven days are you building a game: state machine, progress,
lives, animations, resume behaviour, what happens when they close the tab mid run. That work is
real, but it is your product, not our API.
What actually consumes the time, in order:
Being on Angular, Vue or anything else does not push the number up. The script tag path is
about as much work as the React one, and arguably less, since there is no install and no build
config. What does push it up: you need file or audio answers, so you must supply a
mediaHandler and somewhere to store the files, or you have no public HTTPS endpoint for
webhooks yet, or your backend is not Node, in which case @edpire/sdk/client will not run for
you and you call the REST API directly instead. The token endpoint is a single POST, so this is
a small amount of work in any language.
Things that push it down: you are on Next.js, your learners already have stable user IDs,
and your content is authored before you start writing code.
Before you go live
- Every origin registered, including staging and localhost
- Entitlement checked inside the token endpoint, not only on the page
- Player container has a real height
-
mediaHandlersupplied, if any question takes an upload or a recording -
localeset from the assessment’s language, not your UI language - Webhook signature verified, handler idempotent
- Client reconciliation matches the attempt by ID
- Learner has a next step after submitting
- Open response assessments show provisional scores as provisional
- Tested with a real learner account, on mobile, on a slow connection
- If using the script tag, pinned to an exact SDK version rather than
@latest
Where to go next
Integration Runbook
Who does what, in what order, with dates.
Embedded Player
Every Exam Hall prop and callback.
Custom Flow
Arcade in full detail, with a complete worked example.
Troubleshooting
Blank player, token errors, styles behaving oddly.