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. This is the recommended approach because the server maintains a running total across all of a student’s submissions, using totalLessons — a mandatory field on every Timeback course — as the denominator.
await activity.end({
	xpEarned: 80,
	masteredUnits: 1,
})

pctComplete directly

If your app already tracks overall course progress, you can pass pctComplete (0—100) directly when ending an activity. The server forwards it as-is — no computation needed.
await activity.end({
	xpEarned: 80,
	pctComplete: 70,
})

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. Gets totalLessons from your course config
  2. Adds up past and current masteredUnits
  3. Calculates: round(total / totalLessons * 100), clamped 0–100
  4. Emits the result as pctCompleteApp in the ActivityCompletedEvent
If pctComplete is provided alongside masteredUnits, the server uses pctComplete as-is and does not run the auto-computation. The explicit value always wins.

Configuration

Every Timeback course includes totalLessons in its metadata.metrics. The total number of lessons in the course. This is the denominator used for auto-computation.
timeback.config.json
{
	"courses": [
		{
			"subject": "Math",
			"grade": 3,
			"courseCode": "MATH-3",
			"metadata": {
				"metrics": {
					"totalLessons": 10
				}
			}
		}
	]
}

Example walkthrough

Assume a course has totalLessons: 10. A student completes three activities over time:
SubmissionmasteredUnitsHistorical sumTotal masteredpctCompleteApp
1st30330
2nd23550
3rd25770
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