> ## Documentation Index
> Fetch the complete documentation index at: https://docs.timeback.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> Caliper event types emitted by the Timeback SDK

The Timeback SDK emits [Caliper](https://www.imsglobal.org/activity/caliper)-based events to report learning activity.

<Tip>
  See [Activity Models](/beta/about-timeback/concepts/activity-models) for when each event is
  emitted.
</Tip>

## ActivityCompletedEvent

Emitted once per activity run when the student completes the activity. Carries completion metrics like questions answered, XP earned, and mastery.

```json theme={null}
{
	"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
	"id": "urn:uuid:c51570e4-f8ed-4c18-bb3a-dfe51b2cc594",
	"type": "ActivityEvent",
	"action": "Completed",
	"profile": "TimebackProfile",
	"eventTime": "2026-01-27T10:03:00.000Z",
	"actor": {
		"id": "https://api.example.com/ims/oneroster/rostering/v1p2/users/student-123",
		"type": "TimebackUser",
		"email": "student@example.com"
	},
	"object": {
		"id": "https://myapp.example.com/activities/Math/g3/lesson-1",
		"type": "TimebackActivityContext",
		"subject": "Math",
		"app": { "name": "My App" }
	},
	"generated": {
		"id": "https://api.example.com/ims/metrics/collections/activity/abc-123",
		"type": "TimebackActivityMetricsCollection",
		"items": [
			{ "type": "xpEarned", "value": 80 },
			{ "type": "totalQuestions", "value": 10 },
			{ "type": "correctQuestions", "value": 8 },
			{ "type": "masteredUnits", "value": 1 }
		],
		"extensions": {
			"pctCompleteApp": 67
		}
	},
	"extensions": {
		"runId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
		"courseId": "MATH-3"
	}
}
```

### Activity metrics

The `generated.items` array contains completion metrics. Each item has a `type` (string) and a `value` (number).

<ResponseField name="xpEarned" type="number" required>
  XP earned for this activity. Must follow the [1 XP = 1 focused
  minute](/beta/about-timeback/concepts/xp-system) rule.
</ResponseField>

<ResponseField name="totalQuestions" type="number">
  Total questions in the activity. Optional, but required if `correctQuestions` is provided.
</ResponseField>

<ResponseField name="correctQuestions" type="number">
  Questions answered correctly. Optional, but required if `totalQuestions` is provided.
</ResponseField>

<ResponseField name="masteredUnits" type="number">
  Number of **new** units (lessons) the student mastered during this activity. This is an
  incremental count, not a cumulative total. The server sums these values across submissions and
  uses them to auto-compute `pctCompleteApp`. Optional. See [Course
  progress](/beta/build-on-timeback/sdk/activity-tracking/course-progress) for full details.
</ResponseField>

<ResponseField name="pctCompleteApp" type="number">
  Course completion percentage (0--100). Either passed directly via `pctComplete` in the activity
  payload, or auto-computed by the server from `masteredUnits` and
  [`totalLessons`](/beta/build-on-timeback/reference/configuration#course-progress-config). Sent
  via `generated.extensions.pctCompleteApp`. See [Course
  progress](/beta/build-on-timeback/sdk/activity-tracking/course-progress).
</ResponseField>

## TimeSpentEvent

Emitted periodically during a session (every 15s by default) and on final flush. Each event covers a bounded time window — how many seconds were active vs inactive.

```json theme={null}
{
	"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
	"id": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
	"type": "TimeSpentEvent",
	"action": "SpentTime",
	"profile": "TimebackProfile",
	"eventTime": "2026-01-27T10:00:15.000Z",
	"actor": {
		"id": "https://api.example.com/ims/oneroster/rostering/v1p2/users/student-123",
		"type": "TimebackUser",
		"email": "student@example.com"
	},
	"object": {
		"id": "https://myapp.example.com/activities/Math/g3/lesson-1",
		"type": "TimebackActivityContext",
		"subject": "Math",
		"app": { "name": "My App" }
	},
	"generated": {
		"id": "https://api.example.com/ims/metrics/collections/time-spent/def-456",
		"type": "TimebackTimeSpentMetricsCollection",
		"items": [
			{
				"type": "active",
				"value": 15,
				"startDate": "2026-01-27T10:00:00.000Z",
				"endDate": "2026-01-27T10:00:15.000Z"
			},
			{
				"type": "inactive",
				"value": 0
			}
		]
	},
	"extensions": {
		"runId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
		"courseId": "MATH-3"
	}
}
```

### Time metrics

The `generated.items` array contains time window metrics. Each item has a `type` (string), a `value` in **seconds** (max 86400), and optional fields.

#### Time categories

<ResponseField name="active" type="number">
  Seconds the student was actively engaged (tab visible, not paused).
</ResponseField>

<ResponseField name="inactive" type="number">
  Seconds the student was inactive (paused or tab hidden).
</ResponseField>

<ResponseField name="waste" type="number">
  Seconds classified as non-productive.
</ResponseField>

<ResponseField name="unknown" type="number">
  Seconds that could not be classified.
</ResponseField>

<ResponseField name="anti-pattern" type="number">
  Seconds flagged as anomalous behavior.
</ResponseField>

#### Per-item fields

<ResponseField name="subType" type="string">
  Optional sub-classification providing additional detail on the time category.
</ResponseField>

<ResponseField name="startDate" type="string">
  ISO 8601 timestamp for the start of the time window.
</ResponseField>

<ResponseField name="endDate" type="string">
  ISO 8601 timestamp for the end of the time window.
</ResponseField>

## Shared structure

Both event types share these top-level fields:

<ResponseField name="@context" type="string" required>
  Caliper JSON-LD context. Always `"http://purl.imsglobal.org/ctx/caliper/v1p2"`.
</ResponseField>

<ResponseField name="id" type="string" required>
  Unique event identifier in `urn:uuid:...` format.
</ResponseField>

<ResponseField name="type" type="string" required>
  `"ActivityEvent"` for completions, `"TimeSpentEvent"` for time windows.
</ResponseField>

<ResponseField name="action" type="string" required>
  `"Completed"` for completions, `"SpentTime"` for time windows.
</ResponseField>

<ResponseField name="profile" type="string" required>
  Always `"TimebackProfile"`.
</ResponseField>

<ResponseField name="eventTime" type="string" required>
  ISO 8601 timestamp of when the event occurred.
</ResponseField>

<ResponseField name="actor" type="TimebackUser" required>
  The student who performed the action. See [Identity](/beta/build-on-timeback/sdk/identity).

  <Expandable title="properties">
    <ResponseField name="id" type="string" required>
      URL identifying the user (e.g. OneRoster user URL).
    </ResponseField>

    <ResponseField name="type" type="string" required>
      Always `"TimebackUser"`.
    </ResponseField>

    <ResponseField name="email" type="string" required>
      The student's email address.
    </ResponseField>

    <ResponseField name="name" type="string">
      Display name.
    </ResponseField>

    <ResponseField name="role" type="string">
      One of `student`, `teacher`, `admin`, or `guide`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="object" type="TimebackActivityContext" required>
  The activity context where the event was recorded.

  <Expandable title="properties">
    <ResponseField name="id" type="string" required>
      URL identifying the activity.
    </ResponseField>

    <ResponseField name="type" type="string" required>
      Always `"TimebackActivityContext"`.
    </ResponseField>

    <ResponseField name="subject" type="string" required>
      One of `Reading`, `Language`, `Vocabulary`, `Social Studies`, `Writing`, `Science`,
      `FastMath`, `Math`, `None`, `Other`.
    </ResponseField>

    <ResponseField name="app" type="object" required>
      The application. Must include `name`. Optional `id` (URL) and `extensions`.
    </ResponseField>

    <ResponseField name="course" type="object">
      The course context. Must include `name`. Optional `id` (URL) and `extensions`.
    </ResponseField>

    <ResponseField name="activity" type="object">
      The specific activity. Must include `name`. Optional `id` (URL) and `extensions`.
    </ResponseField>

    <ResponseField name="process" type="boolean">
      Whether to process the event through the ETL pipeline.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="generated" type="object" required>
  Metrics collection. Shape depends on event type:

  <Tabs>
    <Tab title="ActivityCompletedEvent">
      Type: `TimebackActivityMetricsCollection`

      <Expandable title="properties">
        <ResponseField name="id" type="string" required>
          URL identifying this collection.
        </ResponseField>

        <ResponseField name="type" type="string" required>
          Always `"TimebackActivityMetricsCollection"`.
        </ResponseField>

        <ResponseField name="items" type="TimebackActivityMetric[]" required>
          Array of activity metrics. Each item has `type` (`xpEarned`, `totalQuestions`,
          `correctQuestions`, `masteredUnits`) and `value` (number). See [Activity
          metrics](#activity-metrics).
        </ResponseField>

        <ResponseField name="extensions" type="object">
          Additional attributes, e.g. `pctCompleteApp` (course completion percentage,
          0–100).
        </ResponseField>
      </Expandable>
    </Tab>

    <Tab title="TimeSpentEvent">
      Type: `TimebackTimeSpentMetricsCollection`

      <Expandable title="properties">
        <ResponseField name="id" type="string" required>
          URL identifying this collection.
        </ResponseField>

        <ResponseField name="type" type="string" required>
          Always `"TimebackTimeSpentMetricsCollection"`.
        </ResponseField>

        <ResponseField name="items" type="TimeSpentMetric[]" required>
          Array of time metrics. Each item has `type` (`active`, `inactive`, `waste`,
          `unknown`, `anti-pattern`), `value` (seconds), and optional
          `startDate`/`endDate`. See [Time metrics](#time-metrics).
        </ResponseField>

        <ResponseField name="extensions" type="object">
          Additional attributes not defined by the model.
        </ResponseField>
      </Expandable>
    </Tab>
  </Tabs>
</ResponseField>

<ResponseField name="edApp" type="string">
  URL identifying the application context (standard Caliper envelope field).
</ResponseField>

<ResponseField name="extensions" type="object">
  Custom attributes including `runId` and `courseId` for event correlation.
</ResponseField>

Both events also accept these optional [Caliper envelope](https://www.imsglobal.org/activity/caliper) fields:

<ResponseField name="target" type="string">
  Entity representing a particular segment or location within the object.
</ResponseField>

<ResponseField name="referrer" type="string">
  Entity representing the referring context.
</ResponseField>

<ResponseField name="group" type="string | object">
  An Organization representing the group context. Can be a URL or an Organization entity object.
</ResponseField>

<ResponseField name="membership" type="string">
  The relationship between the actor and the group in terms of roles and status.
</ResponseField>

<ResponseField name="session" type="string">
  The current user session.
</ResponseField>

<ResponseField name="federatedSession" type="string">
  If the event occurs within an LTI platform launch, the tool's LtiSession.
</ResponseField>

<CardGroup cols={2}>
  <Card title="Custom Activities" icon="chart-line" href="/beta/build-on-timeback/sdk/activity-tracking/intro">
    How the SDK emits these events
  </Card>

  <Card title="Caliper API" icon="plug" href="/beta/build-on-timeback/clients/caliper">
    Direct Caliper API access for custom event submission
  </Card>
</CardGroup>
