PlaycademyPlaycademy

Tracking Progress

Track student activities and calculate XP

Overview

Track student activities and calculate progress in your learning application.

You'll use Caliper to send activity events and OneRoster to store results in the gradebook.

This data powers dashboards, reports, and adaptive learning features.

Activity tracking runtime workflow showing Caliper events and OneRoster gradebook

Choose Environment

Decide on Staging for development or Production for live apps.

Authenticate

Get an access token using your Timeback credentials (see Authentication).

Send ActivityEvents

Use Caliper to track when students start and complete activities.

Record Results

Use OneRoster to store scores, XP, and mastery data.

Using Playcademy?

Activity tracking is automatic via startActivity() and endActivity().

Environments

Find the Caliper API base URL for your target environment:

EnvironmentBase URL
Staginghttps://caliper-staging.alpha-1edtech.ai
Productionhttps://caliper.alpha-1edtech.ai

Send Activity Events

Timeback uses a custom Caliper profile with two event types:

ActivityEvent

Record when a student completes an activity with accuracy, XP, and mastery data.

The generated.items array can include:

TypeDescription
totalQuestionsThe total number of questions presented in a graded activity
correctQuestionsThe number of questions answered correctly in a graded activity
xpEarnedAwarded Timeback XP points
masteredUnitsNumber of learning units mastered (typically 1 when a student completes a unit above accuracy/time thresholds)
Request
curl -X POST $TIMEBACK_CALIPER_URL/ims/caliper/v1p2/events \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "sensor": "https://your-app.com",    "sendTime": "2025-11-18T10:15:00Z",    "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",    "data": [{      "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",      "id": "urn:uuid:c51570e4-f8ed-4c18-bb3a-dfe51b2cc594",      "type": "ActivityEvent",      "profile": "TimebackProfile",      "eventTime": "2025-11-18T10:15:00Z",      "action": "Completed",      "actor": {        "id": "https://api.alpha-1edtech.ai/ims/oneroster/rostering/v1p2/users/student-123",        "type": "TimebackUser",        "email": "student@example.com"      },      "object": {        "id": "fractions-quiz",        "type": "TimebackActivityContext",        "subject": "Math",        "app": { "name": "Math App" },        "activity": { "id": "fractions-quiz", "name": "Fractions Quiz" },        "course": {          "id": "https://api.alpha-1edtech.ai/ims/oneroster/rostering/v1p2/courses/math-grade-5",          "name": "Math Grade 5"        }      },      "generated": {        "id": "metrics-123",        "type": "TimebackActivityMetricsCollection",        "attempt": 1,        "items": [          { "type": "totalQuestions", "value": 10 },          { "type": "correctQuestions", "value": 8 },          { "type": "xpEarned", "value": 100 },          { "type": "masteredUnits", "value": 1 }        ]      }    }]  }'
const event = {    '@context': 'http://purl.imsglobal.org/ctx/caliper/v1p2',    id: `urn:uuid:${crypto.randomUUID()}`,    type: 'ActivityEvent',    profile: 'TimebackProfile',    eventTime: new Date().toISOString(),    action: 'Completed',    actor: {        id: `${TIMEBACK_API_URL}/ims/oneroster/rostering/v1p2/users/student-123`,        type: 'TimebackUser',        email: 'student@example.com',    },    object: {        id: 'fractions-quiz',        type: 'TimebackActivityContext',        subject: 'Math',        app: { name: 'Math App' },        activity: { id: 'fractions-quiz', name: 'Fractions Quiz' },        course: {            id: `${TIMEBACK_API_URL}/ims/oneroster/rostering/v1p2/courses/math-grade-5`,            name: 'Math Grade 5',        },    },    generated: {        id: 'metrics-123',        type: 'TimebackActivityMetricsCollection',        attempt: 1,        items: [            { type: 'totalQuestions', value: 10 },            { type: 'correctQuestions', value: 8 },            { type: 'xpEarned', value: 100 },            { type: 'masteredUnits', value: 1 },        ],    },}await fetch(`${TIMEBACK_CALIPER_URL}/ims/caliper/v1p2/events`, {    method: 'POST',    headers: {        Authorization: `Bearer ${accessToken}`,        'Content-Type': 'application/json',    },    body: JSON.stringify({        sensor: 'https://your-app.com',        sendTime: new Date().toISOString(),        dataVersion: 'http://purl.imsglobal.org/ctx/caliper/v1p2',        data: [event],    }),})

TimeSpentEvent

Record how much time a student spent learning:

Request
curl -X POST $TIMEBACK_CALIPER_URL/ims/caliper/v1p2/events \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "sensor": "https://your-app.com",    "sendTime": "2025-11-18T10:15:00Z",    "dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",    "data": [{      "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",      "id": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890",      "type": "TimeSpentEvent",      "profile": "TimebackProfile",      "eventTime": "2025-11-18T10:15:00Z",      "action": "SpentTime",      "actor": {        "id": "https://api.alpha-1edtech.ai/ims/oneroster/rostering/v1p2/users/student-123",        "type": "TimebackUser",        "email": "student@example.com"      },      "object": {        "id": "fractions-quiz",        "type": "TimebackActivityContext",        "subject": "Math",        "app": { "name": "Math App" },        "course": {          "id": "https://api.alpha-1edtech.ai/ims/oneroster/rostering/v1p2/courses/math-grade-5",          "name": "Math Grade 5"        }      },      "generated": {        "id": "time-metrics-123",        "type": "TimebackTimeSpentMetricsCollection",        "items": [          { "type": "active", "value": 240 },          { "type": "inactive", "value": 60 }        ]      }    }]  }'
const event = {    '@context': 'http://purl.imsglobal.org/ctx/caliper/v1p2',    id: `urn:uuid:${crypto.randomUUID()}`,    type: 'TimeSpentEvent',    profile: 'TimebackProfile',    eventTime: new Date().toISOString(),    action: 'SpentTime',    actor: {        id: `${TIMEBACK_API_URL}/ims/oneroster/rostering/v1p2/users/student-123`,        type: 'TimebackUser',        email: 'student@example.com',    },    object: {        id: 'fractions-quiz',        type: 'TimebackActivityContext',        subject: 'Math',        app: { name: 'Math App' },        course: {            id: `${TIMEBACK_API_URL}/ims/oneroster/rostering/v1p2/courses/math-grade-5`,            name: 'Math Grade 5',        },    },    generated: {        id: 'time-metrics-123',        type: 'TimebackTimeSpentMetricsCollection',        items: [            { type: 'active', value: 240 },            { type: 'inactive', value: 60 },        ],    },}await fetch(`${TIMEBACK_CALIPER_URL}/ims/caliper/v1p2/events`, {    method: 'POST',    headers: {        Authorization: `Bearer ${accessToken}`,        'Content-Type': 'application/json',    },    body: JSON.stringify({        sensor: 'https://your-app.com',        sendTime: new Date().toISOString(),        dataVersion: 'http://purl.imsglobal.org/ctx/caliper/v1p2',        data: [event],    }),})

Record Results with XP

Store student results with XP, accuracy, and mastery data using OneRoster.

Create AssessmentLineItem

Define what's being graded (e.g., a quiz or activity).

Request
curl -X POST $TIMEBACK_API_URL/ims/oneroster/gradebook/v1p2/assessmentLineItems \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "assessmentLineItem": {      "status": "active",      "title": "Fractions Quiz",      "componentResource": {        "sourcedId": "fractions-quiz-resource"      }    }  }'
const lineItem = {    status: 'active',    title: 'Fractions Quiz',    componentResource: {        sourcedId: 'fractions-quiz-resource',    },}await fetch(`${TIMEBACK_API_URL}/ims/oneroster/gradebook/v1p2/assessmentLineItems`, {    method: 'POST',    headers: {        Authorization: `Bearer ${accessToken}`,        'Content-Type': 'application/json',    },    body: JSON.stringify({ assessmentLineItem: lineItem }),})

Create AssessmentResult

Store the student's score, XP, accuracy, and mastery progress.

The metadata field can include custom data like xp, accuracy, and masteredUnits.

Request
curl -X POST $TIMEBACK_API_URL/ims/oneroster/gradebook/v1p2/assessmentResults \  -H "Authorization: Bearer $ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{    "assessmentResult": {      "status": "active",      "assessmentLineItem": {        "sourcedId": "fractions-quiz-line-item"      },      "student": {        "sourcedId": "student-123"      },      "score": 85,      "scoreStatus": "fully graded",      "scoreDate": "2025-11-18T10:15:00Z",      "metadata": {        "xp": 100,        "accuracy": 85,        "totalQuestions": 10,        "correctQuestions": 8.5,        "masteredUnits": 1      }    }  }'
const result = {    status: 'active',    assessmentLineItem: {        sourcedId: 'fractions-quiz-line-item',    },    student: {        sourcedId: 'student-123',    },    score: 85,    scoreStatus: 'fully graded',    scoreDate: new Date().toISOString(),    metadata: {        xp: 100,        accuracy: 85,        totalQuestions: 10,        correctQuestions: 8.5,        masteredUnits: 1,    },}await fetch(`${TIMEBACK_API_URL}/ims/oneroster/gradebook/v1p2/assessmentResults`, {    method: 'POST',    headers: {        Authorization: `Bearer ${accessToken}`,        'Content-Type': 'application/json',    },    body: JSON.stringify({ assessmentResult: result }),})

XP Calculation

1 XP = 1 minute of active learning time. See Activities & XP for the full breakdown.

XP is awarded based on accuracy and attempt number. The 80% accuracy threshold ensures students have demonstrated mastery before earning XP.

First attempt:

AccuracyMultiplierDescription
100%1.25xBonus for perfect accuracy
80-99%1.0xFull XP for mastery
< 80%0xNo XP (mastery not demonstrated)

Re-attempts (diminishing returns to encourage focused first attempts):

AttemptMultiplier
1st re-attempt0.5x
2nd re-attempt0.25x
3rd+ re-attempts0x

What's Next?

On this page