Skip to main content
@edpire/sdk/client exports EdpireClient, a typed wrapper over the Edpire REST API. It runs server-side only (your API key must never reach the browser), has zero browser dependencies, and uses native fetch. It is not a rendering pattern — it’s the backend utility used alongside the Embedded Player and Custom Flow.

Setup

Create the client once at module level and reuse it — don’t instantiate it per request.
import { EdpireClient } from "@edpire/sdk/client"

// module level — one instance, reused across all requests
const client = new EdpireClient({
  apiKey: process.env.EDPIRE_API_KEY!,   // starts with edp_live_
  baseUrl: "https://edpire.com",         // optional, this is the default
})

EdpireClientOptions

OptionTypeRequiredDescription
apiKeystringYesYour Edpire API key (edp_live_…). Server-side only.
baseUrlstringNoAPI base URL. Defaults to https://edpire.com.
fetchtypeof fetchNoCustom fetch — for testing or edge runtimes (e.g. Cloudflare Workers).

Method reference

All methods are async, hit /api/v1 on baseUrl with Authorization: Bearer <apiKey>, and throw EdpireError on non-2xx.

Assessments

MethodReturnsNotes
getAssessments(params?)PaginatedResponse<AssessmentSummary>params: status, ids (bulk, max 50), page, limit. No exercises in summaries.
getAssessment(id)AssessmentFull content with exercises[].questions[].content_ast. Never includes answer keys.
const { items, total } = await client.getAssessments({ status: "published", page: 1, limit: 20 })

// Bulk fetch up to 50 IDs in one call (passing more than 50 throws a 400)
const { items: batch } = await client.getAssessments({ ids: ["id1", "id2", "id3"] })

// Single assessment with exercises + questions
const assessment = await client.getAssessment("assessment-uuid")

Submit & check

MethodReturnsNotes
submit(assessmentId, options)GradeResultRecords + grades a full attempt. Fires submission.graded. Accepts flat StoredAnswer[] or nested answers.
checkQuestion(assessmentId, options)CheckResultGrades one question. Stateless (no record). Rate-limited per (session_id, question_id).
// Full submission — answers can be a flat StoredAnswer[] (auto-nested) ...
const result = await client.submit("assessment-uuid", {
  learner_ref: "user-123",
  answers: stored,
})

// ... or the nested format directly:
const result2 = await client.submit("assessment-uuid", {
  learner_ref: "user-123",
  answers: {
    exerciseAnswers: [
      { exerciseId: "ex-1", questionAnswers: [
        { questionId: "q-1", answers: [{ nodeId: "n1", type: "choiceSet", value: ["opt_a"] }] },
      ]},
    ],
  },
})

// Single-question check (Custom Flow)
const check = await client.checkQuestion("assessment-uuid", {
  exercise_id: "ex-1",
  question_id: "q-1",
  answers: [{ nodeId: "n1", type: "choiceSet", value: ["opt_a"] }],
  learner_ref: "user-123",
  session_id: "attempt-uuid",      // generate once per attempt
  include_correct_answers: true,   // reveal correct answers (set server-side)
})
SubmitOptions also accepts allow_draft (submit against draft assessments for testing) and metadata (consumer-side data, not stored by Edpire).

Submissions & learner history

MethodReturnsNotes
getSubmission(id)SubmissionFull submission with per-question results.
getLearnerResults(learnerRef, params?)PaginatedResponse<Submission>All submissions for a learner. params: page, limit.
const submission = await client.getSubmission("submission-uuid")
const { items } = await client.getLearnerResults("user-123", { page: 1, limit: 50 })

Collections

MethodReturnsNotes
getCollections(params?)PaginatedResponse<Collection>List collections (with item_count).
getCollection(id)CollectionDetailIncludes ordered items[].assessment.
getCollectionResults(id, params?)PaginatedResponse<Submission & { assessment_title }>Flat results across every assessment in the collection.
const { items: collections } = await client.getCollections()
const detail = await client.getCollection("collection-uuid")   // detail.items[].assessment
const { items: results } = await client.getCollectionResults("collection-uuid")

Embed tokens

MethodReturnsNotes
mintEmbedToken(assessmentId, learnerRef)EmbedTokenSingle-use token (expires 1h) for the Embedded Player.
const { token, expires_at } = await client.mintEmbedToken("assessment-uuid", "user-123")

Webhooks

MethodReturnsNotes
registerWebhook(url, events)WebhookWithSecretSecret shown once — store it. http://localhost allowed for dev.
listWebhooks()Webhook[]Secrets not included.
deleteWebhook(id)void
const webhook = await client.registerWebhook(
  "https://yourplatform.com/webhooks/edpire",
  ["submission.graded", "assessment.published", "assessment.content_updated"],
)
console.log(webhook.secret)   // store securely — shown once
See Webhooks for the full event list and signature verification.

Error handling

Every method throws EdpireError on a non-2xx response:
import { EdpireError } from "@edpire/sdk/client"

try {
  await client.getAssessment("bad-id")
} catch (err) {
  if (err instanceof EdpireError) {
    console.log(err.status)   // e.g. 404
    console.log(err.message)  // e.g. "Assessment not found"
  }
}
Common status codes: 400 bad request · 401 invalid API key · 403 forbidden / origin not allowed · 404 not found · 409 max attempts reached · 422 grading failed · 429 rate limit exceeded.

Custom fetch (testing / edge runtimes)

const client = new EdpireClient({
  apiKey: "test-key",
  baseUrl: "http://localhost:3001",
  fetch: customFetch,   // injectable for tests or Cloudflare Workers
})

Type reference

All response and option types (Assessment, GradeResult, CheckResult, Submission, Collection, etc.) are exported from @edpire/sdk/client. See the type catalog.