Skip to main content
Course progress tells the platform how far a student is through your app’s notion of a course. For example, “Student A is 70% through Math Grade 3.”

Two approaches

You can report progress in two ways. Both are passed as part of completion metrics (client) or timeback.activity.record() (server). If your app tracks mastery at the lesson level, pass masteredUnits and let the server auto-compute pctComplete for you. The SDK reads historical EduBridge analytics and uses a positive totalLessons in the selected course configuration as its denominator. It does not maintain an atomic running total.

pctComplete directly

If your app already tracks overall course progress, you can pass pctComplete (0—100) directly when ending an activity. The SDK skips historical computation for an explicit value. Keep it within 0–100: browser submissions clamp out-of-range numbers, while the TypeScript server API rejects them.

How masteredUnits works

masteredUnits is an incremental count; it represents how many new units (lessons) a student mastered during this specific activity, not a cumulative total.
masteredUnits must be incremental. Sending cumulative counts will double-count mastery and inflate the student’s progress percentage.

What the server does

When masteredUnits is provided and pctComplete is omitted, the server:
  1. Resolves a positive totalLessons, preferring environment-specific metadata over base metadata.
  2. Finds the student’s enrollment for the synced course and reads its EduBridge enrollment facts.
  3. Sums historical mastered units. It adds the current count only if weekly facts do not already contain the same activity ID and course/enrollment context. If that weekly check fails, it conservatively omits the current count.
  4. Computes an integer percentage and clamps it to 0–100, then emits pctCompleteApp in the ActivityCompletedEvent.
If the enrollment, denominator, or historical facts cannot be resolved, automatic progress can be omitted. Analytics freshness and concurrent submissions can affect the result. The weekly-fact check is not a transaction or a global exactly-once guarantee. TypeScript uses Math.round; Python uses round, which differs on exact half ties.
If pctComplete is provided alongside masteredUnits, the SDK validates the explicit value for the relevant API and does not run the auto-computation. The explicit value always wins.

Configuration

Configure metadata.metrics.totalLessons for courses that use automatic progress. The fragment below shows the relevant fields; include the other required app/course settings from the configuration reference.
Relevant configuration fields

Example walkthrough

Assume a course has totalLessons: 10, historical facts are up to date, and each activity is new for this enrollment. A student completes three activities over time: After the third submission, the student is 70% through the course.

Best practices

When a student masters one lesson, send masteredUnits: 1. If they master two lessons in the same activity session, send masteredUnits: 2. The value always represents how many new units were mastered during this activity — never a running total.
If a student replays a lesson they already mastered, do not send masteredUnits again for that lesson. Double-counting inflates the student’s progress percentage because the server sums all historical values.
If no lessons were mastered during the activity, omit masteredUnits entirely (or send 0). The server only runs the auto-computation when masteredUnits is a number greater than zero.

Next steps

Custom Activities reference

Full API for activity.end() and timeback.activity.record()

Events

Caliper event schemas including ActivityCompletedEvent

Configuration

timeback.config.json reference including totalLessons

Stateful activities

Multi-session activities where completion is recorded server-side