How it works
Once started, the SDK sends periodic TimeSpentEvents throughout the session. When the student finishes, the SDK sends one final TimeSpentEvent followed by an ActivityCompletedEvent. All events share the samerunId for correlation.
Starting an activity
Every single-session activity starts the same way:Ending an activity
How you end an activity depends on whether the student completed the activity. In both cases, accumulated time data is flushed. The difference is whether a completion result is also recorded.With completion
When a student finishes, callactivity.end() with metrics:
- Flushes a final heartbeat (
TimeSpentEvent) - Submits the completion (
ActivityCompletedEvent)
Without completion
Callactivity.end() without arguments to flush time data only; in this case, no completion event is recorded.
- Flushes a final heartbeat (
TimeSpentEvent) - Stops the heartbeat timer
- Cleanup: the component unmounts before the student finishes (e.g. navigating away mid-activity). You want to flush accumulated time without recording a result.
- Stateful activities: the activity spans multiple sessions, so the client only tracks time per visit. Completion is recorded by the server when the student eventually finishes.
Framework integration
- React
- Vue
- Svelte
- Solid
Completion metrics
When ending an activity with completion, include metrics:XP earned for this activity. Must follow the 1 XP = 1 focused
minute rule.
Total questions in the activity. Optional, but required if
correctQuestions is provided.Questions answered correctly. Optional, but required if
totalQuestions is provided.Units mastered. Optional.
Completion percentage, 0–100. Optional.
totalQuestions and correctQuestions must be provided together.If you provide one, you must provide the other.xpEarned is required for completion.Use activity.end() with no args for time-only flushes.Time override
By default, the SDK tracks time automatically. For advanced scenarios where you need to override the timing (e.g., offline sync or batch imports), use thetime parameter:
Best practices
Start activities in useEffect/onMount
Start activities in useEffect/onMount
Always start activities in lifecycle hooks to ensure proper cleanup.
Flush time on cleanup, complete on user action
Flush time on cleanup, complete on user action
Use
activity.end() (no args) in cleanup functions. Only call activity.end(metrics) when
the student has actually finished the activity.Handle missing timeback client
Handle missing timeback client
Check if the timeback client exists before creating activities (it may be null if not
authenticated).
Use meaningful activity IDs
Use meaningful activity IDs
Use stable, unique IDs that identify the specific lesson or content piece.