Telemetry & xAPI (1.7.x)¶
LessonKit emits versioned telemetry events from @lessonkit/react and maps them to xAPI via @lessonkit/xapi.
Event catalog¶
Version:
telemetryCatalogV3Version = 3(exported from@lessonkit/core; v1 catalog usestelemetryCatalogVersion = 1)JSON:
@lessonkit/core/telemetry-catalog.v3.json(must matchbuildTelemetryCatalogV3()in tests)Types: discriminated
TelemetryEventwith requiredcourseIdon every event
Event |
When |
Key |
|---|---|---|
|
First provider mount per tab session + course |
— |
|
|
— |
|
Lesson becomes active |
|
|
Lesson completed |
|
|
With |
|
|
Quiz choice selected |
|
|
First correct answer (built-in Quiz) or |
|
|
P0 assessment blocks |
|
|
Custom UI / branching via |
|
|
Compound navigation |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Compound navigation (1.2+) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Session fields on all events: sessionId, optional attemptId, optional user.
xAPI mapping¶
Canonical mapper: telemetryEventToXAPIStatement(event) in @lessonkit/xapi.
Telemetry |
xAPI verb |
Object URN |
|---|---|---|
|
initialized |
|
|
completed |
|
|
initialized |
|
|
completed |
|
|
(none) |
Returns |
|
answered |
|
|
completed |
|
|
answered |
|
|
completed |
|
|
experienced |
|
|
experienced |
|
|
experienced |
|
|
experienced |
|
|
experienced |
|
|
experienced |
|
|
experienced |
|
|
experienced |
|
|
experienced |
|
|
completed |
|
|
experienced |
|
|
experienced |
|
|
experienced |
|
|
completed |
|
|
experienced |
|
|
experienced |
|
React runtime: after each track(), the provider calls the mapper and xapi.send(statement) when a statement is returned (single path; no duplicate lifecycle helpers).
Prefer createFetchTransport from @lessonkit/xapi for production LRS delivery (timeout, retry backoff, keepalive exitTransport for pagehide). See Telemetry & xAPI guide.
Custom interactions and blocks¶
For block-level xAPI on interaction events:
Set
blockIdonScenario/Reflection(or passblockIdin interaction payload).Ensure an active
lessonId(normal when insideLesson).Call
track("interaction", { kind: "…", blockId: "my-block", … }).
Without blockId, interaction events are tracked but do not emit xAPI.
Batching and buffer limits¶
When config.tracking.batch.enabled is true (or batchSink is set), events are queued in an in-memory buffer before delivery.
Setting |
Default |
Behavior |
|---|---|---|
|
|
Periodic flush while the sink is slow or unavailable |
|
|
Flush when the buffer reaches this size |
Internal telemetry buffer cap |
1000 events |
When full, new events are dropped until the buffer drains |
If the sink throws or rejects, failed events are re-queued (entire batch for batchSink; undelivered tail for per-event sinks). Under prolonged outage the telemetry buffer can hit the cap; production is silent unless you wire config.observability.onTelemetryBufferDrop. Monitor sink failures via onTelemetrySinkError (covers both sink and batchSink). See production checklist.
The xAPI in-memory queue (default 1000 statements) drops the oldest statement when full — wire onXapiQueueCap and onXapiQueueDepth.
Non-batched mode (batch.enabled: false) invokes sink synchronously per event with no buffer cap.
Tab exit and pagehide¶
LessonkitProvider calls flushOnExit (keepalive batch / xAPI delivery when configured) then async flush on visibilitychange (hidden) and pagehide. Wire exitTransport from createFetchTransport and exitBatchSink from createFetchBatchSink for best-effort delivery when the tab closes.
Identity¶
All events require courseId. Lesson-scoped events require lessonId. Component ids are trimmed at the React provider boundary (assertValidId) so telemetry payloads and xAPI URNs stay aligned. See Identity reference.
course_started dedupe¶
The runtime uses separate session-storage marks:
Key pattern |
Purpose |
|---|---|
|
xAPI / session bootstrap (may fire before tracking sink is ready) |
|
Tracking sink delivery — set only after a successful tracking |
|
Non-tracking pipeline (xAPI mapper, bridge, extra sinks) |
When config.session.sessionId changes, migrateCourseStartedMark moves dedupe state to the new session id so learners do not receive duplicate course_started events after LMS handoff.
Quiz and assessment telemetry¶
Built-in Quiz / KnowledgeCheck must be wrapped in <Lesson>. Events without an enclosing lessonId are dropped by tryBuildTelemetryEvent.
P0 assessment blocks (TrueFalse, FillInTheBlanks, etc.) emit assessment_answered / assessment_completed. Legacy Quiz emits quiz_answered / quiz_completed. Both namespaces map to xAPI; LMS bridge forwarding uses bridge.track for answered events and submitAssessment for completion (see Packaging reference).
LMS bridge (LXPack)¶
When config.lxpack.bridge is "auto", completion events (course_completed, lesson_completed, assessment_completed, quiz_completed) call the parent lxpackBridge.v1 API. Answered events (assessment_answered, quiz_answered) forward via bridge.track. Wire observability.onLxpackBridgeMiss and onLxpackBridgeError in production.
Production observability¶
Required hooks depend on what you enable — see the production checklist for the full matrix. When both tracking and xAPI delivery are configured, wire all six hooks including onXapiTransportError (required for xAPI, not optional):
Config |
Required hooks |
|---|---|
Tracking or xAPI enabled |
|
Tracking delivery ( |
+ |
xAPI delivery ( |
+ |
Without these hooks, buffer/queue drops and sink failures are silent in production builds.