Skip to main content

Client

lessons.list()

Fetches available lessons from the courses configured in timeback.config.json.
course
ActivityCourseRef
Filter to a specific configured course. If omitted, returns lessons across all configured courses.

Return type

{
	id: string // Unique lesson identifier
	name: string // Display name
	type: LessonType // 'powerpath-100' | 'quiz' | 'test-out' | ...
	courseId: string // Parent course ID
	questionCount: number // Total questions in the lesson
	metadata: object // Additional lesson metadata
}

Examples

const lessons = await timeback.lessons.list()

lessons.start()

Starts or resumes a lesson session. Returns a LessonSession that manages the question loop and time tracking. An activity tracker starts automatically, so TimeSpentEvent heartbeats begin immediately.
lessonId
string
required
The lesson to start.
course
ActivityCourseRef
required
Course reference for time tracking. Must match a course in timeback.config.json. Use either subject + grade or a course code.
lessonType
LessonType
Determines how the SDK handles the question loop and completion. Pass lesson.type from the discovery result. The two primary types behave differently:
  • powerpath-100: adaptive (questions served one at a time, difficulty adjusts, session completes when score reaches 100)
  • quiz: static (all questions fetched in a batch, session completes via explicit finalization)
Other quiz-like types (test-out, placement, unit-test) follow the same static pattern as quiz.
name
string
Display name for the activity tracker. Defaults to the lesson name.
forceNew
boolean
default:"false"
Start a fresh attempt instead of resuming an existing one. Without this, lessons.start() resumes the student’s current in-progress attempt.When true, the behavior depends on the lesson type:
  • Quiz-like types (quiz, test-out, etc.): the current attempt is finalized first (scored with whatever was answered), then a new attempt is created
  • powerpath-100: PowerPath attempts cannot be finalized via the API, so a new attempt is created directly

Examples

const session = await timeback.lessons.start({
	lessonId: lesson.id,
	lessonType: lesson.type,
	course: { subject: 'Math', grade: 5 },
})

Lesson session

The object returned by lessons.start(). Manages the question loop, scoring state, and time tracking for a single lesson attempt.

Properties

Most properties are set once when the session is created. score and finalized are exceptions: they update live as the student progresses.
lessonId
string
The lesson identifier passed to lessons.start().
lessonType
LessonType
The delivery mode for this session. Resolved from the server response, falling back to the lessonType you passed to lessons.start(), then to 'quiz'.Controls how the SDK fetches questions (next() hits the server each time for adaptive, fetches all at once for linear) and how completion works (adaptive auto-completes at score 100, linear requires explicit finalization).
attempt
number | undefined
The PowerPath attempt number (1, 2, 3, …). undefined on first-ever access when no prior attempt data exists.
score
number
Cumulative score for the current attempt. Starts at the value returned by PowerPath (0 for new attempts, or the saved score when resuming).Updates live after each submit() call and again after complete() with the final score. For powerpath-100, this is a 0–100 mastery scale where reaching 100 means the lesson auto-completes. For quiz types, the score typically updates at finalization.
questionCount
number | undefined
Total questions in the lesson, as reported by PowerPath. For linear lessons (quiz, test-out, etc.), this is the fixed question count. For adaptive lessons (powerpath-100), this may be undefined since the total varies based on the student’s performance.
seenQuestions
number
Number of questions the student had already seen when the session started. For powerpath-100, reflects how many questions were served before the session was resumed. For linear lesson types, always 0 (linear quizzes track answered questions differently via their local buffer).
finalized
boolean
Whether the lesson attempt has been finalized. When true, scores are locked and the gradebook is updated.Initial value depends on lesson type: for powerpath-100, always starts false (adaptive sessions are never pre-finalized). For quiz types, reflects whether the attempt was already completed when resuming.Updates live: set to true by submit() when the server signals completion (e.g., powerpath-100 reaching score 100), and by complete() with the server’s finalization result.

Methods

next()
Promise<LessonQuestion | null>
Returns the next question, or null when there are no more questions.Behavior depends on lesson type:
  • powerpath-100 (adaptive): each call makes a server round-trip. PowerPath selects the next question based on the student’s performance so far. Returns null when PowerPath signals completion (score reached 100).
  • quiz and other linear types: the first call fetches all questions from the server in a single batch and buffers them locally. Subsequent calls return the next unanswered question from the buffer with no network request. Already-answered questions (from a resumed attempt) are skipped automatically.
Returns null immediately if the session is already completed.
submit(input)
Promise<LessonSubmitResult>
Submits the student’s answer to PowerPath and returns the scored result.After each call, session.score is updated with the server’s response. If the server signals that the lesson is complete (e.g., powerpath-100 reaching score 100), session.finalized is set to true and subsequent next() calls return null.For powerpath-100, correct reflects real-time scoring (the server evaluates immediately). For quiz types, correct is always false during the quiz. Evaluation happens at finalization.
complete()
Promise<LessonSessionCompleteResult>
Finalizes the lesson in two steps:
  1. Server: calls PowerPath’s finalization endpoint to lock in the score, calculate accuracy, and trigger gradebook writes. For quiz types, this is where scoring actually happens. For powerpath-100, the score is already computed; this step confirms it.
  2. Client: calls activity.end() to flush the final time tracking heartbeat. If the flush fails, the result still contains valid scoring data. timeTrackingSent will be false and error will describe what went wrong.
Updates session.score and session.finalized with the server’s final values.
pause()
Flushes any accumulated time to the server, then stops heartbeats until resume() is called. No-op if already paused or if the session has ended. Paused time is not counted as active learning time.
resume()
Starts a fresh time tracking window and restarts heartbeats. No-op if not currently paused or if the session has ended.

LessonQuestion

The object returned by session.next():
{
	id: string             // Unique question identifier
	title?: string         // Optional display title
	difficulty?: string    // Optional difficulty level
	content: {
		rawXml: string     // QTI XML content for rendering
	}
}
The content.rawXml field contains QTI XML. Your app is responsible for parsing and rendering this.
QTI XML parsing utilities are coming soon to the SDK. In the meantime, you’ll need to parse the XML yourself to extract prompts, choices, and correct responses.

LessonSubmitResult

The object returned by session.submit():
{
	correct: boolean // Whether the answer was correct
	score: number // Updated cumulative score
	complete: boolean // Whether the lesson is now complete
}

LessonSessionCompleteResult

The object returned by session.complete():
{
	score: number              // Final score
	totalQuestions: number     // Total questions
	correctQuestions: number   // Answered correctly
	accuracy: number           // Percentage (0-100)
	finalized: boolean         // Whether PowerPath finalized the attempt
	lessonType: LessonType     // The lesson type
	timeSpentSeconds: number   // Active learning time
	timeTrackingSent: boolean  // Whether the final time flush succeeded
	error?: string             // Error message if time flush failed
}
timeTrackingSent can be false even when the lesson completed successfully. The scoring data is always reliable; only the last ~15s of time data may be missing. See Handle time tracking errors.

lessons.attempts()

Lists all attempts for a lesson.
lessonId
string
required
The lesson to list attempts for.

Return type

{
	attempt: number // Attempt number (1, 2, 3, ...)
	score: number // Final score for this attempt
	finalized: boolean // Whether the attempt was completed
	totalQuestions: number
	correctQuestions: number
}

lessons.attemptDetails()

Gets per-question data for a specific attempt. The response is a discriminated union based on lesson type.
lessonId
string
required
The lesson to review. Same id from the discovery result or the session’s lessonId.
attempt
number
required
Which attempt to review (1, 2, 3, …). Get available attempt numbers from lessons.attempts().

Return type

Adaptive lessons (powerpath-100):
{
	lessonType: 'powerpath-100'
	seenQuestions: LessonAttemptQuestion[]
	score: number
	totalQuestions: number
	correctQuestions: number
}
Linear lessons (quiz, test-out, etc.):
{
	lessonType: 'quiz' | 'test-out' | ...
	questions: LessonAttemptQuestion[]
	score: number
	totalQuestions: number
	correctQuestions: number
}

LessonAttemptQuestion

{
	id: string // Question identifier
	correct: boolean // Whether the student answered correctly
	studentResponse: string // The student's submitted answer
	content: {
		rawXml: string // QTI XML for re-rendering
	}
}

getLessonAttemptQuestions()

Helper that normalizes the discriminated attemptDetails() response into a flat LessonAttemptQuestion[]:
import { getLessonAttemptQuestions } from '@timeback/sdk'

const details = await timeback.lessons.attemptDetails({
	lessonId: lesson.id,
	attempt: 1,
})

const questions = getLessonAttemptQuestions(details)
// Always returns LessonAttemptQuestion[] regardless of lesson type

Server

The server-side lessons namespace exposes the same operations without session management or automatic time tracking. Each operation is a standalone async function call — your backend controls the lifecycle directly. See the FastAPI or Next.js adapter docs for server setup.
The server namespace uses lesson and student identifiers directly, unlike the client API which uses lessonId and resolves the student from the authenticated session.

timeback.lessons.list(input?)

Lists lessons from the configured courses. Same behavior as the client lessons.list().
course
ActivityCourseRef
Filter to a specific configured course. Accepts { subject, grade } or { code }. If omitted, returns lessons across all configured courses.

Examples

const lessons = await timeback.lessons.list()
const mathLessons = await timeback.lessons.list({ course: { subject: 'Math', grade: 5 } })

timeback.lessons.start(input)

Starts or resumes a lesson attempt. Unlike the client’s lessons.start(), this returns a plain result object — no LessonSession or time tracking.
lesson
string
required
The lesson identifier (from lessons.list()).
student
string
required
The student’s Timeback ID.
forceNew
boolean
default:"false"
Start a fresh attempt instead of resuming. Behaves the same as the client forceNew option.

Return type

{
	lessonId: string
	lessonType: LessonType
	attempt?: number
	score: number
	questionCount?: number
	seenQuestions: number
	finalized: boolean
}

Examples

const result = await timeback.lessons.start({ lesson: 'lesson-1', student: 'tb_abc' })
const fresh = await timeback.lessons.start({ lesson: 'lesson-1', student: 'tb_abc', forceNew: true })

timeback.lessons.next(input)

Returns the next question(s) for a lesson attempt. The return type depends on lesson type:
  • powerpath-100: returns a single LessonQuestion
  • All other types: returns a LessonQuestionBatch with all questions
lesson
string
required
The lesson identifier.
student
string
required
The student’s Timeback ID.
lessonType
LessonType
Pass "powerpath-100" for adaptive single-question delivery. Omit for batch delivery.

Return type

// Single question (powerpath-100)
LessonQuestion

// Batch (quiz, test-out, etc.)
{
	questions: LessonQuestion[]
	answeredIds: string[]
	score: number
	finalized: boolean
	complete: boolean
}

Examples

// Quiz — returns all questions as a batch
const batch = await timeback.lessons.next({ lesson: 'lesson-1', student: 'tb_abc' })

// Adaptive — returns one question at a time
const question = await timeback.lessons.next({
	lesson: 'lesson-1',
	student: 'tb_abc',
	lessonType: 'powerpath-100',
})

timeback.lessons.submit(input)

Submits a student’s answer to a lesson question.
lesson
string
required
The lesson identifier.
student
string
required
The student’s Timeback ID.
question
string
required
The question ID being answered (from LessonQuestion.id).
response
string
required
The student’s selected answer.

Return type

{
	correct: boolean
	score: number
	complete: boolean
	questionResult?: unknown
}

Examples

const result = await timeback.lessons.submit({
	lesson: 'lesson-1',
	student: 'tb_abc',
	question: 'q-42',
	response: 'B',
})

timeback.lessons.complete(input)

Finalizes a lesson attempt — locks in the score and triggers gradebook writes. Unlike the client’s session.complete(), this does not flush time tracking.
lesson
string
required
The lesson identifier.
student
string
required
The student’s Timeback ID.

Return type

{
	lessonType: LessonType
	attempt?: number
	score: number
	totalQuestions?: number
	correctQuestions?: number
	accuracy?: number
	finalized: boolean
}

Examples

const result = await timeback.lessons.complete({ lesson: 'lesson-1', student: 'tb_abc' })

timeback.lessons.attempts(input)

Lists all attempts for a lesson/student pair. Same return type as the client lessons.attempts().
lesson
string
required
The lesson identifier.
student
string
required
The student’s Timeback ID.

Examples

const attempts = await timeback.lessons.attempts({ lesson: 'lesson-1', student: 'tb_abc' })

timeback.lessons.attemptDetails(input)

Returns raw progress data for a specific attempt. See client lessons.attemptDetails() for the discriminated return shape.
lesson
string
required
The lesson identifier.
student
string
required
The student’s Timeback ID.
attempt
number
required
Which attempt to review (1, 2, 3, …).

Examples

const details = await timeback.lessons.attemptDetails({
	lesson: 'lesson-1',
	student: 'tb_abc',
	attempt: 1,
})