xAPI & Analytics API Reference
xAPI statement storage, H5P event handling, learner progress tracking, and multi-level analytics (content, student, class). All xAPI data is stored in PostgreSQL with JSONB columns for flexible querying.
xAPI Statement Endpoints
Base path: /api/xapi
POST /api/xapi/statements
Store one or more xAPI statements.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | None |
Description
Accepts a single xAPI statement or an array of statements. Each statement's actor is enriched with the authenticated user's account information (overwriting actor.account with { homePage, name: userId }). Statements are validated for required fields: actor must have mbox or account, verb.id is required, object.id is required. If no id is provided, a UUID is auto-generated. If no timestamp is provided, the current time is used. Duplicate statement IDs are silently ignored (ON CONFLICT DO NOTHING).
Each stored statement emits a 'statement' event on the xAPI service EventEmitter, enabling downstream listeners (e.g., AGS score passback).
Request
Single statement:
{
"actor": {
"objectType": "Agent",
"account": { "homePage": "https://example.com", "name": "user-1" }
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/completed",
"display": { "en-US": "completed" }
},
"object": {
"objectType": "Activity",
"id": "https://app.creatiq.com/h5p/content/abc123",
"definition": {
"type": "http://adlnet.gov/expapi/activities/assessment",
"name": { "en-US": "Biology Quiz" }
}
},
"result": {
"score": { "scaled": 0.85, "raw": 85, "max": 100 },
"success": true,
"completion": true,
"duration": "PT120S"
},
"context": {
"platform": "EduAgentic H5P",
"language": "en"
}
}
Or an array of statements: [ { ... }, { ... } ]
Response
{
"success": true,
"data": {
"ids": ["uuid-1", "uuid-2"]
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 401 | Unauthorized | Missing or invalid JWT |
| 500 | Failed to store statement | actor missing mbox/account, verb.id missing, or object.id missing |
POST /api/xapi/h5p-event
Handle H5P xAPI events in simplified format.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | None |
Description
A convenience endpoint that accepts a simplified H5P event payload and converts it to a full xAPI statement. The verb string is mapped to a standard xAPI verb IRI:
| Verb String | xAPI Verb IRI |
|---|---|
attempted | http://adlnet.gov/expapi/verbs/attempted |
answered | http://adlnet.gov/expapi/verbs/answered |
completed | http://adlnet.gov/expapi/verbs/completed |
passed | http://adlnet.gov/expapi/verbs/passed |
failed | http://adlnet.gov/expapi/verbs/failed |
interacted | http://adlnet.gov/expapi/verbs/interacted |
progressed | http://adlnet.gov/expapi/verbs/progressed |
| (unknown) | Falls back to interacted |
The generated statement uses:
- Actor: authenticated user's account
- Object:
{baseUrl}/h5p/content/{contentId}with activity typeassessment - Context: platform
"EduAgentic H5P", language"en" - Duration: converted to ISO 8601 format (
PT{seconds}S)
Request
{
"contentId": "abc123",
"verb": "completed",
"score": {
"scaled": 0.85,
"raw": 85,
"max": 100
},
"success": true,
"completion": true,
"duration": 120,
"response": "selected-choice-2"
}
| Field | Type | Required | Description |
|---|---|---|---|
contentId | string | Yes | H5P content identifier |
verb | string | Yes | Verb name (see mapping above) |
score | object | No | Score with scaled, raw, max |
success | boolean | No | Whether the attempt was successful |
completion | boolean | No | Whether the content was completed |
duration | number | No | Duration in seconds |
response | string | No | Learner response data |
Response
{
"success": true,
"data": {
"id": "uuid"
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Missing required fields: contentId, verb | Missing required fields |
| 401 | Unauthorized | Missing or invalid JWT |
GET /api/xapi/statements
Query stored xAPI statements with filters.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | None |
Description
Retrieves xAPI statements matching the provided filters. Results are ordered by timestamp descending. All filter parameters are optional; omitting all filters returns all statements (up to limit). Actor matching checks both actor.account.name and actor.mbox.
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
actorId | string | No | Filter by actor account name or mbox |
verbId | string | No | Filter by verb IRI |
objectId | string | No | Filter by object/activity ID |
since | string (ISO 8601) | No | Only statements after this timestamp |
until | string (ISO 8601) | No | Only statements before this timestamp |
limit | number | No | Maximum number of results |
Response
{
"success": true,
"data": {
"statements": [
{
"id": "uuid",
"actor": { "objectType": "Agent", "account": { "homePage": "...", "name": "..." } },
"verb": { "id": "http://adlnet.gov/expapi/verbs/completed", "display": { "en-US": "completed" } },
"object": { "objectType": "Activity", "id": "..." },
"result": { "score": { "scaled": 0.85 }, "success": true, "completion": true },
"context": { "platform": "EduAgentic H5P" },
"timestamp": "2025-01-15T11:30:00.000Z"
}
]
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 401 | Unauthorized | Missing or invalid JWT |
| 500 | Failed to query statements | Database error |
xAPI Progress Endpoints
GET /api/xapi/progress/:contentId
Get the authenticated user's progress on a specific content item.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | None |
Path Parameters
| Param | Type | Description |
|---|---|---|
contentId | string | Content/activity identifier |
Response
{
"success": true,
"data": {
"userId": "user-12345",
"contentId": "abc123",
"attempts": 3,
"bestScore": 92,
"lastScore": 0,
"completed": true,
"passed": true,
"totalDuration": 0,
"lastAttemptAt": "2025-01-15T11:30:00.000Z"
}
}
When no progress exists:
{
"success": true,
"data": {
"userId": "user-12345",
"contentId": "abc123",
"attempts": 0,
"bestScore": 0,
"lastScore": 0,
"completed": false,
"passed": false,
"totalDuration": 0,
"message": "No progress recorded yet"
}
}
GET /api/xapi/progress
Get all content progress for the authenticated user.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | None |
Response
{
"success": true,
"data": {
"progress": [
{
"userId": "user-12345",
"contentId": "abc123",
"attempts": 3,
"bestScore": 92,
"lastScore": 0,
"completed": true,
"passed": true,
"totalDuration": 0,
"lastAttemptAt": "2025-01-15T11:30:00.000Z"
},
{
"userId": "user-12345",
"contentId": "def456",
"attempts": 1,
"bestScore": 60,
"lastScore": 0,
"completed": false,
"passed": false,
"totalDuration": 0,
"lastAttemptAt": "2025-01-14T09:00:00.000Z"
}
]
}
}
xAPI Content & User Analytics
GET /api/xapi/analytics/content/:contentId
Get aggregate analytics for a specific content item.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | None |
Description
Computes aggregate metrics across all learners for a content item. Scores are calculated from result.score.scaled on completed verb statements. Pass rate is passed / completed * 100.
Path Parameters
| Param | Type | Description |
|---|---|---|
contentId | string | Content/activity identifier |
Response
{
"success": true,
"data": {
"contentId": "abc123",
"totalAttempts": 150,
"totalCompletions": 120,
"averageScore": 78.5,
"averageDuration": 0,
"passRate": 85.0,
"lastUpdated": "2025-01-15T11:30:00.000Z"
}
}
When no data exists:
{
"success": true,
"data": {
"contentId": "abc123",
"totalAttempts": 0,
"totalCompletions": 0,
"averageScore": 0,
"averageDuration": 0,
"passRate": 0,
"message": "No analytics data available yet"
}
}
GET /api/xapi/analytics/user
Get analytics summary for the authenticated user.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | None |
Description
Returns a learner analytics summary: total content attempted/completed, average score, and identification of strong areas (score >= 80) and weak areas (score > 0 and < 50).
Response
{
"success": true,
"data": {
"userId": "user-12345",
"totalContentsAttempted": 15,
"totalContentsCompleted": 12,
"averageScore": 76.3,
"strongAreas": ["content-a", "content-b"],
"weakAreas": ["content-x"],
"learningTime": 0
}
}
Analytics Dashboard Endpoints
Base path: /api/analytics
These endpoints power the teacher analytics dashboard and require the analytics-dashboard feature gate.
GET /api/analytics/class-overview
Get aggregate analytics for a teacher's class.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | analytics-dashboard |
Description
Returns a class-level overview including total students, weekly active count, average completion rate, top-performing content (up to 5), weakest topics (score < 60%, up to 5), and a 30-day activity heatmap. The query scopes to content that the authenticated teacher has interacted with (attempted).
Response
{
"success": true,
"data": {
"totalStudents": 45,
"activeThisWeek": 38,
"averageCompletionRate": 72,
"topPerformingContent": [
{ "contentId": "https://app.creatiq.com/h5p/content/abc", "title": "Cell Biology Quiz", "avgScore": 92 },
{ "contentId": "https://app.creatiq.com/h5p/content/def", "title": "Math Basics", "avgScore": 88 }
],
"weakestTopics": [
{ "topic": "Organic Chemistry", "avgScore": 42, "studentCount": 30 },
{ "topic": "Calculus II", "avgScore": 38, "studentCount": 25 }
],
"activityHeatmap": [
{ "date": "2025-01-01", "activityCount": 120 },
{ "date": "2025-01-02", "activityCount": 95 }
]
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 401 | Unauthorized | Missing or invalid JWT |
| 403 | Feature not available | analytics-dashboard not enabled |
GET /api/analytics/content/:contentId
Get detailed analytics for a specific content item.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | analytics-dashboard |
Description
Returns detailed per-content analytics including attempt counts, unique student count, average score, completion rate, average duration, and a per-question breakdown showing correct rates and attempt counts. Question breakdown is derived from answered verb statements where result.response is present.
Path Parameters
| Param | Type | Description |
|---|---|---|
contentId | string | Content/activity identifier (min 1 char) |
Response
{
"success": true,
"data": {
"contentId": "abc123",
"totalAttempts": 150,
"uniqueStudents": 45,
"averageScore": 78,
"completionRate": 80,
"averageDuration": 145,
"questionBreakdown": [
{ "questionId": "q1-choice-a", "correctRate": 92, "avgAttempts": 1 },
{ "questionId": "q2-choice-b", "correctRate": 45, "avgAttempts": 3 },
{ "questionId": "q3-fill-blank", "correctRate": 68, "avgAttempts": 2 }
]
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Validation error: ... | Empty contentId parameter |
| 401 | Unauthorized | Missing or invalid JWT |
| 403 | Feature not available | analytics-dashboard not enabled |
GET /api/analytics/student/:studentId
Get analytics for a specific student.
| Property | Value |
|---|---|
| Auth | JWT Bearer token (authMiddleware) |
| Feature gate | analytics-dashboard |
Description
Returns a student's overall score, completed activity count, recent activity (up to 20 most recent xAPI statements), and per-topic performance breakdown.
Path Parameters
| Param | Type | Description |
|---|---|---|
studentId | string | Student/learner identifier (min 1 char) |
Response
{
"success": true,
"data": {
"studentId": "user-12345",
"overallScore": 76,
"completedActivities": 12,
"recentActivity": [
{
"id": "uuid",
"actor": { "objectType": "Agent", "account": { "homePage": "...", "name": "user-12345" } },
"verb": { "id": "http://adlnet.gov/expapi/verbs/completed" },
"object": { "id": "https://app.creatiq.com/h5p/content/abc123" },
"result": { "score": { "scaled": 0.85 } },
"timestamp": "2025-01-15T11:30:00.000Z"
}
],
"topicPerformance": [
{ "topic": "Cell Biology Quiz", "score": 92 },
{ "topic": "Math Basics", "score": 88 },
{ "topic": "Organic Chemistry", "score": 42 }
]
}
}
Errors
| Status | Error | Condition |
|---|---|---|
| 400 | Validation error: ... | Empty studentId parameter |
| 401 | Unauthorized | Missing or invalid JWT |
| 403 | Feature not available | analytics-dashboard not enabled |
xAPI Data Types
XAPIStatement
interface XAPIStatement {
id?: string; // UUID, auto-generated if omitted
actor: XAPIActor; // Who performed the action
verb: XAPIVerb; // What action was performed
object: XAPIObject; // What the action was performed on
result?: XAPIResult; // Outcome of the action
context?: XAPIContext; // Additional context
timestamp?: string; // ISO 8601, defaults to now
stored?: string; // ISO 8601, set by server
authority?: XAPIActor; // Who asserts this statement
version?: string; // xAPI version (default: "1.0.3")
}
XAPIActor
interface XAPIActor {
objectType?: "Agent" | "Group";
mbox?: string; // mailto: IRI
mbox_sha1sum?: string;
openid?: string;
account?: {
homePage: string; // Account system URL
name: string; // Account identifier
};
name?: string; // Display name
}
XAPIResult
interface XAPIResult {
score?: {
scaled?: number; // -1 to 1, normalized score
raw?: number; // Actual score
min?: number; // Minimum possible
max?: number; // Maximum possible
};
success?: boolean;
completion?: boolean;
response?: string; // Learner response data
duration?: string; // ISO 8601 duration (e.g., "PT120S")
}
Database Table
xapi_statements
| Column | Type | Description |
|---|---|---|
id | UUID (PK) | Statement ID |
actor | JSONB | xAPI actor object |
verb | JSONB | xAPI verb object |
object | JSONB | xAPI activity/object |
result | JSONB | Score, success, completion, duration |
context | JSONB | Platform, language, registration |
timestamp | TIMESTAMPTZ | When the event occurred |
stored | TIMESTAMPTZ | When stored in database |
authority | JSONB | Asserting authority |
version | VARCHAR(20) | xAPI spec version |
full_statement | JSONB | Complete original statement |
Indexes:
idx_xapi_actor_name-- GIN index onactor->'account'->'name'idx_xapi_verb_id-- B-tree onverb->>'id'idx_xapi_object_id-- B-tree onobject->>'id'idx_xapi_timestamp-- B-tree ontimestamp DESC
Standard Verb IRIs
| Constant | IRI |
|---|---|
ATTEMPTED | http://adlnet.gov/expapi/verbs/attempted |
ANSWERED | http://adlnet.gov/expapi/verbs/answered |
COMPLETED | http://adlnet.gov/expapi/verbs/completed |
PASSED | http://adlnet.gov/expapi/verbs/passed |
FAILED | http://adlnet.gov/expapi/verbs/failed |
INTERACTED | http://adlnet.gov/expapi/verbs/interacted |
PROGRESSED | http://adlnet.gov/expapi/verbs/progressed |