XP System

Understand TimeBack's XP (eXperience Points) system to correctly award and track student progress across learning apps.

What XP Represents

XP is TimeBack's universal metric for tracking productive learning effort across all subjects, grades, and apps.

Core principle: 1 XP ≈ 1 minute of focused, productive learning effort from a typical student.

Key distinctions:

  • XP measures effort, not progress. A student can earn XP while their mastery percentage stays flat (reviewing material) or increases (learning new material). These are separate metrics.
  • XP is cumulative. Once earned, XP never decreases due to knowledge decay. Knowledge state can regress (students forget), but XP remains as a permanent record of effort.
  • XP is content-intrinsic. Each piece of content has a fixed "expected XP" value that's the same for all students, regardless of how long an individual student takes.

Setting Expected XP

Every piece of content in your app (lessons, quizzes, assessments) should have an expected XP value—the amount a student earns for completing it at mastery.

How to calibrate expected XP:

  1. Have a competent person complete the content correctly while being timed ("speed-running")
  2. Round the time to the nearest minute—that's your expected XP
  3. Do not use student averages (they include off-task time and struggling students)

Example: A lesson takes a competent adult 12 minutes to complete thoughtfully → Expected XP = 12

This approach ensures XP remains consistent across your content and aligned with TimeBack's 1 XP ≈ 1 minute baseline.

When to Award XP

Award XP when learning is verified, not when activities are merely completed.

Activity Type When to Award XP
Lessons with quiz When quiz is completed at mastery threshold
Standalone assessments When submitted at mastery threshold
Passive content (videos, articles) Only after a quiz verifies learning—include the video/article time in the quiz's XP
Practice exercises When completed at mastery threshold

Do not award XP for:

  • Incomplete activities
  • Activities completed below mastery threshold (unless using partial credit for reattempts)
  • Passive consumption without verification (watching a video doesn't prove learning)

Mastery Thresholds

TimeBack uses accuracy-based mastery thresholds to determine when learning has occurred:

Activity Type Mastery Threshold
Lessons and practice 80% accuracy
Quizzes and assessments 90% accuracy

Below these thresholds, students haven't demonstrated mastery at the rigor required. Award 0 XP for sincere effort below threshold, or negative XP for gaming/cheating (see below).

How Much XP to Award

Use this decision table to determine XP awards based on performance and attempt number:

Scenario XP Award
First attempt, 100% accuracy Full expected XP + 20-25% bonus
First attempt, at/above mastery threshold Full expected XP
First attempt, below mastery threshold (sincere effort) 0 XP
First reattempt, achieves mastery 50% of expected XP
Second reattempt, achieves mastery 25% of expected XP
Third+ reattempt, achieves mastery 0 XP
Gaming/cheating detected Negative XP (see below)

Bonus XP for perfect performance: Award 20-25% bonus XP when students achieve 100% accuracy on their first attempt. This incentivizes careful, focused work.

Partial credit on reattempts: When students fail initially but achieve mastery on a retry, award reduced XP. This encourages getting it right the first time while still rewarding eventual mastery.

Negative XP

Award negative XP when students demonstrate insincere effort—gaming, cheating, or deliberately rushing through content without learning.

Examples of gaming patterns:

  • Rapid random guessing (answering faster than reading is possible)
  • Using calculators or LLMs to avoid learning
  • Systematic patterns suggesting answer-lookup
  • Deliberate failure to exploit reattempt mechanics

Calibration principle: Genuine students who are trying should rarely (if ever) receive negative XP. Only penalize clear patterns of bad faith, not individual mistakes or struggling learners.

The exact negative XP amount should reflect the severity—typically -2 to -5 XP per incident.

Sending XP Events

Send XP awards to TimeBack using Caliper GradeEvent with scoreType: "XP".

Example event:

{
  "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
  "id": "urn:uuid:event-unique-id",
  "type": "GradeEvent",
  "actor": "urn:uuid:student-platform-id",
  "action": "Graded",
  "object": {
    "id": "urn:uuid:xp-attempt-id",
    "type": "Attempt",
    "assignee": "urn:uuid:student-platform-id",
    "assignable": {
      "id": "https://yourapp.com/lessons/fractions-intro",
      "type": "AssignableDigitalResource",
      "mediaType": "curriculum/lesson",
      "name": "Introduction to Fractions"
    }
  },
  "generated": {
    "id": "urn:uuid:score-xp-id",
    "type": "Score",
    "scoreType": "XP",
    "attempt": "urn:uuid:xp-attempt-id",
    "scoreGiven": 15
  },
  "eventTime": "2024-01-15T14:30:00.000Z",
  "edApp": "urn:uuid:your-app-id",
  "session": "urn:tag:auto-attach"
}

For complete event schema details, see Caliper Events Reference.

Reading XP Data

After sending XP events, you can read back XP entries to display progress in your app or verify events were processed.

Required Scope

Request the events read scope when registering:

scope: https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.readonly

Fetch XP Entries

const response = await fetch(
  `https://platform.timeback.com/xp/1.0/users/${userId}/entries?applicationId=${process.env.TIMEBACK_APP_ID}`,
  {
    headers: { Authorization: `Bearer ${accessToken}` },
  },
);

const { entries, total, limit, offset } = await response.json();

Response Schema

{
  "entries": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "value": 15,
      "userId": "student-platform-id",
      "applicationId": "your-app-id",
      "curriculumItemId": "lesson-id-if-provided",
      "dateGenerated": "2024-01-15T14:30:00.000Z",
      "sourceEventId": "caliper-event-id",
      "sourceEvent": {
        "id": "caliper-event-id",
        "extensions": {
          "activityType": "LESSON_QUIZ",
          "accuracy": 92
        }
      }
    }
  ],
  "total": 42,
  "limit": 10,
  "offset": 0
}
Field Type Description
id uuid Unique identifier for this XP entry
value number XP amount (can be negative)
userId uuid Student who earned/lost the XP
applicationId uuid App that generated the XP
curriculumItemId uuid Optional curriculum item associated with the XP
dateGenerated datetime When the XP was awarded
sourceEventId uuid Caliper event ID that created this entry
sourceEvent.id uuid Same as sourceEventId
sourceEvent.extensions object Custom extensions from the source Caliper event (nullable)

Query Parameters

Parameter Type Description
applicationId uuid Filter to XP from your app only
curriculumItemId uuid Filter to XP from a specific curriculum item
after datetime Filter entries generated after this timestamp (ISO 8601)
before datetime Exclude entries at or after this instant (exclusive upper bound on source event time)
limit integer Maximum entries to return (1-100, default 10)
offset integer Number of entries to skip (default 0)

Day summary by CASE subject

To display today's net XP grouped by canonical CASE subject, call the day-summary endpoint. The platform resolves the org-local calendar day from the student's school organization timeZone config (parent orgs inherit; the client does not send a time zone). The response includes window (UTC bounds of that org-local day), subjects (one bucket per resolved CASE subject), unassigned (XP the resolver could not map to a subject), and totalXp (net of all origins, including negative manual adjustments).

const response = await fetch(`https://platform.timeback.com/xp/1.0/users/${userId}/day-summary?date=2026-09-22`, {
  headers: { Authorization: `Bearer ${accessToken}` },
});

const { date, timeZone, window, totalXp, subjects, unassigned } = await response.json();

Omit date to use today in the resolved time zone. To audit a day summary against individual rows, call entries with after set to window.start and before set to window.end (the same half-open interval the summary uses).

Use GET /xp/1.0/users/{userId}/entries when you need the paginated audit list of individual ledger rows.

Daily XP per student and subject for an organization

To show a school month as a calendar, one row per student and one cell per subject per day, call the organization endpoint instead of the per-student reads. It returns net platform-ledger XP per student, per CASE subject, per school-local day for the organization and every organization below it.

const params = new URLSearchParams({ startDate: '2026-10-01', endDate: '2026-10-31' });
let cursor: string | null = null;
do {
  if (cursor !== null) params.set('cursor', cursor);
  const response = await fetch(`https://platform.timeback.com/xp/1.0/orgs/${orgId}/daily-subject-xp?${params}`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  const page = await response.json();
  // page.students[].days[] holds one cell per day with XP
  cursor = page.nextCursor;
} while (cursor !== null);

Each page lists up to limit students (default and maximum 1,000) in ascending id order. A page also ends early when its response would pass about 4 MB, so a dense page holds fewer students and never exceeds the response size limit. A month for a 2,300-student organization takes at most three requests. Read pages until nextCursor is null. The range is inclusive and at most 31 days. A response page looks like this:

{
  "orgId": "3e0866b3-8f28-4e6f-9310-ec79c46b865a",
  "startDate": "2026-10-01",
  "endDate": "2026-10-31",
  "subjects": [{ "subjectId": "8f2c1b0e-5d41-4c1a-9a53-2f0c7a6d9e11", "title": "Math" }],
  "students": [
    {
      "userId": "b3c5e0aa-1f0e-4a52-8d4f-6a1c2b7f9d20",
      "days": [
        {
          "date": "2026-10-02",
          "totalXp": 41,
          "subjects": [{ "subjectId": "8f2c1b0e-5d41-4c1a-9a53-2f0c7a6d9e11", "xp": 30 }],
          "unassigned": { "xp": 11 }
        }
      ]
    },
    { "userId": "0d9e7a64-3b8c-4f27-a1e5-9c4b2d6f8a13", "days": [] }
  ],
  "nextCursor": "eyJhZnRlclVzZXJJZCI6IjBkOWU3YTY0LTNiOGMtNGYyNy1hMWU1LTljNGIyZDZmOGExMyJ9"
}
  • Who is listed. Every user with a student role in the organization or any organization below it during the range. A student with no XP appears with days: []. A student enrolled in two schools of the tree is listed once. A day counts for a student only when they held a student role in the tree on that day, so a student who moves between schools mid-range appears under each school only for the days they were enrolled there. A role's end date counts as a day of enrollment.

  • Entries not yet attributed. unattributedEntries counts the page's students' live ledger entries, across all dates rather than the requested range, that have no school-local day yet. They are absent from days, so a non-zero value means the totals may be incomplete.

  • Which day. The calendar date of the XP event in the time zone configured on the student's school as of the event's UTC day. A school with no timeZone configured, on it or on any parent organization, is cut at America/Chicago midnight. The zone is the one in force on the event's UTC day, so on the day a student changes to a school in another time zone, an evening event can land on a different date here than in day-summary. Set the school's timeZone before reading it with this endpoint.

  • Subjects. subjects on a page lists the title of every subject that page uses. XP whose subject cannot be resolved is in unassigned. totalXp is net of every origin, including negative manual adjustments. Days with no XP are omitted.

  • Which apps. Only XP written to the platform ledger is returned. An app whose XP the platform does not credit is absent from the response, not zero. Apps with a live crediting path:

    Source of the XP Apps
    Calculated by the platform MobyMax, Zearn, Vocab Loco, Freckle, Lalilo, AlphaRead, Anton, Membean, Egumpp, Alpha Test (placement)
    Reported by the app TimeBack Anywhere, TimeBack Dash, Math Academy, AlphaWrite, AlphaNumbers, AlphaLiteracy, Math Raiders, Math Cakes, Incept (credited to AlphaMath), Alpha Test (instruments)

    Literably, Nice Academy, Edmentum, Edia and Acely currently credit little or no XP to the ledger, so a student's XP in those apps is absent or near zero.

  • Subject against day-summary. This endpoint reads the subject stored when the XP was written, while day-summary resolves it when you read. After a curriculum subject mapping changes, the two can name different subjects for the same XP until the stored subjects are re-resolved.

  • Access. The same events.readonly scope as the day summary. A caller restricted to specific organizations receives 403 for an organization outside that list, and 404 for an organization that does not exist.

Change Notifications via Webhooks

Instead of polling the entries endpoint for changes, subscribe to the xp.created, xp.updated, and xp.deleted webhook events. Each delivery carries { ledgerEntryId, userId, applicationId } — refetch the entry from GET /xp/1.0/users/{userId}/entries to read its current value. See Webhooks — XP Events for payloads, routing, signature verification, and the reconciliation caveat for operator-run ledger cleanups.

See the API Reference for complete endpoint documentation.


Level 3: Caliper Events

Implementation guide for sending Caliper events, including XP awards.

Caliper Events Reference

Complete event schemas and field documentation for XP and other event types.

Session Management

Understand how urn:tag:auto-attach works and other session attribution options.

Authentication

Obtain OAuth credentials required for sending events and reading XP data.

Webhooks

Receive xp.created, xp.updated, and xp.deleted events when ledger entries change, instead of polling the entries endpoint.