Choose the approach that matches your needs.
| Approach | Learner stays on your page? | You build the UI? |
|---|
| Hosted Redirect | No | No |
| Embedded Player | Yes | No |
| Custom Flow | Yes | Yes |
This page is the
decision guide. For full, typed reference on the components and client that power Embedded Player and Custom Flow, see the
SDK Reference.
Hosted Redirect
The simplest integration — no SDK required on the frontend. Redirect learners to Edpire’s hosted assessment UI. Edpire handles the entire learner experience, then redirects back to your app with results.
How it works:
- Publish an assessment in the Edpire dashboard and note its share code (e.g.,
ABC123)
- Build a redirect URL and send the learner there
- After completion, Edpire redirects back to your
return_url with results appended
Redirect URL:
https://{your-slug}.edpire.com/take/{shareCode}?learner_ref={userId}&return_url={yourUrl}
| Parameter | Required | Description |
|---|
shareCode | Yes | Assessment share code from the dashboard |
learner_ref | Recommended | Your stable internal user ID |
return_url | Recommended | Where to send the learner after completion (must be in Allowed Origins) |
Return URL parameters appended by Edpire:
https://yourplatform.com/results?submission_id=sub_xxx&score=14&max_score=20
When to use: Fast adoption, no custom UI needed, comfortable with a browser redirect.
Embedded Player
Mount Edpire’s full assessment player directly inside your page. The learner never leaves your app. The SDK fetches the assessment, renders the UI, grades the submission, and shows per-question feedback — all automatically.
Requires: @edpire/sdk (browser) + @edpire/sdk/client (server)
Server — mint a token:
import { EdpireClient } from "@edpire/sdk/client"
const client = new EdpireClient({ apiKey: process.env.EDPIRE_API_KEY! })
const { token } = await client.mintEmbedToken(assessmentId, userId)
// Pass token to the browser via props or an API response
Browser — mount the player:
import { EdpireAssessment } from "@edpire/sdk"
const embed = EdpireAssessment.mount({
token,
container: "#assessment-root",
onComplete: (result) => {
console.log(`Score: ${result.score}/${result.max_score}, Passed: ${result.passed}`)
},
onError: (err) => console.error(err.message),
})
When to use: You want the full assessment experience inside your page with a single function call. Zero UI code required.
Custom Flow
Build a fully custom learner experience — Duolingo-style flows, flashcard drills, practice modes — using Edpire’s question renderer and the /check endpoint for real-time per-question grading.
Requires: @edpire/sdk + @edpire/sdk/react (browser) + @edpire/sdk/client (server)
Step 1 — Fetch the assessment (server-side proxy)
// GET /api/edpire/assessment/[id]/route.ts
import { EdpireClient } from "@edpire/sdk/client"
const client = new EdpireClient({ apiKey: process.env.EDPIRE_API_KEY! })
const assessment = await client.getAssessment(assessmentId)
return NextResponse.json({ data: assessment })
Step 2 — Flatten into a question sequence
import { flattenAssessment } from "@edpire/sdk"
const steps = flattenAssessment(assessment)
// steps[i] = { exerciseId, questionId, content, points, sequenceNumber, index }
Step 3 — Render questions
import { EdpireQuestion } from "@edpire/sdk/react"
import type { RuntimeAnswer } from "@edpire/sdk"
const [answers, setAnswers] = useState<RuntimeAnswer[]>([])
<EdpireQuestion
content={step.content}
onAnswersChange={setAnswers}
feedback={feedback} // null until the learner checks their answer
dir="ltr" // or "rtl" for Arabic
/>
Step 4 — Grade each question via your backend proxy
// POST /api/edpire/check/[id]/route.ts
// Note: the assessment ID goes in the URL path, not the request body.
const result = await fetch(`https://edpire.com/api/v1/assessments/${assessmentId}/check`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.EDPIRE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
exercise_id: exerciseId,
question_id: questionId,
answers,
learner_ref: userId,
session_id: sessionId, // UUID generated once per attempt
include_correct_answers: true,
}),
}).then((r) => r.json())
// result.data = { correct, score, max_score, feedback }
Prefer the typed helper:
client.checkQuestion(assessmentId, options) from
@edpire/sdk/client handles URL construction for you. See
Custom Flow.
Step 5 — Submit the full attempt when done
The /check endpoint is stateless and does not create submission records. Submit the full answer set after all questions:
import { EdpireClient } from "@edpire/sdk/client"
import type { StoredAnswer } from "@edpire/sdk"
const client = new EdpireClient({ apiKey: process.env.EDPIRE_API_KEY! })
const result = await client.submit(assessmentId, {
learner_ref: userId,
answers: stored, // StoredAnswer[] — no manual nesting required
})
Rate limiting: Each (session_id, question_id) pair is limited to 3 checks per hour (configurable per org). Generate a fresh session_id UUID per attempt.
When to use: Custom UX (Duolingo-style, flashcards, hearts/lives), immediate per-question feedback, full control over pacing and navigation.