Level 4: Insights API

Deprecated: The /insights/1.0 read routes on this page are deprecated. Move to the Signals routes with Insights to Signals Migration.

Catalog v1 names take effect on TODO(announced date). Until then the API returns the previous names listed in the slug alias table.

Access coaching insights generated from student learning activity to power dashboards, reports, and intervention workflows.

What Insights Provides

The Insights API gives you read access to coaching insights that TimeBack generates from Caliper events (Level 3). By integrating with Insights, you enable:

  • Student behavior dashboards showing time-on-task and waste patterns
  • Session-level analytics with detailed insight breakdowns
  • Trend analysis over configurable time ranges
  • Cross-session aggregations for progress tracking

Without the Insights API, you can send learning events but can't programmatically access the insights TimeBack generates. With Insights, you can build custom dashboards and integrate coaching data into your app's existing analytics.

For aggregate reporting across students and orgs, see Analytics. To decide between the REST API, an MCP server, and webhooks for a given job, see Choosing an Integration Surface. Published changes appear in the Changelog.

Prerequisites

Before implementing Insights:

  1. Implement Level 3 (Caliper Events)—insights are generated from Caliper events you send
  2. Request OAuth credentials with the events.readonly scope (see Authentication Guide)

API Endpoints

The Insights API provides these endpoints for accessing coaching data:

Endpoint Description
GET /insights/1.0/users/{userId} Get insights for a specific user
GET /insights/1.0/users/{userId}/sessions Get session list for a user
GET /insights/1.0/sessions/{sessionId} Get insights for a specific session
POST /external-access/1.0/sessions/{sessionId}/shares Mint a parent-facing Vault share URL for a session
GET /insights/1.0/users/{userId}/overview Get aggregated trend and breakdown data
GET /insights/1.0/sessions Get org sessions with waste metrics, type counts, and a selectable enrichment set
GET /insights/1.0/types Get the insight type catalog visible to your credentials, optionally by category
GET /signals/1.0/insights/groups Get the published Insight Group vocabulary (slug, display name, family, membership)

The first four cover reporting and dashboards and are the subject of the sections below. The last two support type discovery; see Insight Types.

Authentication

All Insights endpoints require OAuth 2.0 authentication. Request the events read scope:

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

Minting a session share is the exception: it needs the share.manage scope instead (see Mint a Session Share). Listing Insight Groups is the other: it needs the https://timeback-platform.trilogy.com/signals/scope/insights.readonly scope (see Insight Groups).

Include your access token in requests:

const response = await fetch(`https://platform.timeback.com/insights/1.0/users/${userId}`, {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
  },
});

Get User Insights

Retrieve insights for a specific user with optional filtering and pagination.

GET /insights/1.0/users/{userId}

Query Parameters

Parameter Type Required Description
applicationId uuid No Filter insights to a specific learning app
after datetime No Inclusive lower bound on session start time (RFC 3339, offsets allowed)
before datetime No Inclusive upper bound on session start time (RFC 3339, offsets allowed)
sessionId string No CSV of up to 64 Caliper session IRIs (percent-encoded). Exclusive with after / before. A named id owned by another user 403s the whole request; unknown and revoked ids are omitted from a 200.
limit integer No Maximum items to return (1-1000, default 20)
offset integer No Number of items to skip (default 0)
type string No Comma-separated active catalog v1 slugs to filter by. Retired slugs are invalid.
insightGroups string No Comma-separated Insight Group slugs (see below). Overrides category and the unfiltered result.
category string No Comma-separated categories (e.g., PhysicalEnvironment,LearningBehaviour). Narrows the unfiltered result.

Example Request

const userId = 'student-platform-id'; // From LTI 'sub' or roster lookup
const response = await fetch(
  `https://platform.timeback.com/insights/1.0/users/${userId}?limit=10&after=2025-01-01T00:00:00Z`,
  {
    headers: { Authorization: `Bearer ${accessToken}` },
  },
);

const data = await response.json();

Example Response

{
  "session": {
    "startedAtTime": "2025-01-15T09:00:00Z",
    "endedAtTime": "2025-01-15T10:30:00Z",
    "durationInSeconds": 5400,
    "wasteDurationInSeconds": 720,
    "wastePercentage": 13
  },
  "insights": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "insightType": "Socializing",
      "insightGroup": "DistractionFreeSpace",
      "reason": "Conversation detected during a learning block",
      "startedAtTime": "2025-01-15T09:15:00Z",
      "endedAtTime": "2025-01-15T09:20:00Z",
      "durationInSeconds": 300,
      "version": "v1",
      "rolloutState": "NOTIFY"
    }
  ],
  "offset": 0,
  "limit": 10,
  "total": 1
}

Get User Sessions

Retrieve sessions for a user with optional filtering.

GET /insights/1.0/users/{userId}/sessions

Query Parameters

Parameter Type Required Description
applicationId uuid No Filter sessions to a specific learning app
startedAfter datetime No Lower bound for session start time (inclusive)
startedBefore datetime No Upper bound for session start time (inclusive)
category string No Comma-separated categories (e.g., PhysicalEnvironment,LearningBehaviour). Narrows the unfiltered result.
insightGroups string No Comma-separated Insight Group slugs (see below). On this endpoint, excludes sessions with no caller-visible insight of a member type and scopes returned metrics to the filter. category scopes metrics only and never removes a session.
isProctored boolean No Filter sessions by proctored status.
limit integer No Maximum items to return (1-100, default 20)
offset integer No Number of items to skip (default 0)

Example Request

const response = await fetch(
  `https://platform.timeback.com/insights/1.0/users/${userId}/sessions?startedAfter=2025-01-01T00:00:00Z`,
  {
    headers: { Authorization: `Bearer ${accessToken}` },
  },
);

const data = await response.json();

Example Response

{
  "sessions": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "applicationId": "learning-app-uuid",
      "startedAtTime": "2025-01-15T09:00:00Z",
      "endedAtTime": "2025-01-15T10:30:00Z",
      "durationInSeconds": 5400,
      "wasteDurationInSeconds": 720,
      "wastePercentage": 13,
      "isProctored": true,
      "proctoredResult": "PASSED",
      "recordingStartedAtTime": "2025-01-15T09:00:05Z",
      "recordingEndedAtTime": "2025-01-15T10:29:55Z",
      "recordingS3Key": "recordings/2025/01/15/session-550e8400.webm"
    }
  ],
  "offset": 0,
  "limit": 20,
  "total": 1
}

Get Session Insights

Retrieve insights for a specific session.

GET /insights/1.0/sessions/{sessionId}

When no session row exists yet for the given sessionId (for example, immediately after a session starts but before the asynchronous Caliper ingestion pipeline has materialized it), this endpoint returns 200 with an empty result (insights: [], total: 0, and a default session summary with zero duration). Polling clients should treat the empty result as "no data yet" rather than as an error.

Path Parameters

Parameter Type Required Description
sessionId uuid Yes Session ID from the sessions list

Query Parameters

Parameter Type Required Description
isProctored boolean No Filter insights by proctored status.
limit integer No Maximum items to return (1-100, default 20)
offset integer No Number of items to skip (default 0)
type string No Comma-separated active catalog v1 slugs. Retired slugs are invalid.
insightGroups string No Comma-separated Insight Group slugs (see below). Overrides category and the unfiltered result.
category string No Comma-separated categories (e.g., PhysicalEnvironment,LearningBehaviour). Narrows the unfiltered result.

Insight Record Fields

Each insight object returned by GET /insights/1.0/users/{userId} and GET /insights/1.0/sessions/{sessionId} has the following properties:

Field Type Description
id string (uuid) Unique identifier for the insight
insightType string Catalog v1 slug of the insight type
insightGroup string | null Published Insight Group slug for this type
reason string Human-readable description of the finding
startedAtTime datetime When the insight period started
endedAtTime datetime When the insight period ended
durationInSeconds integer Duration of the insight in seconds
version string Insight schema version
messageVersion integer | null Pinned catalog message bundle version, or null when unavailable
rolloutState string | null Rollout stage recorded at detection (NOTIFY, ENFORCE, or SHADOW). New findings default to SHADOW when no snapshot stage is available. SHADOW findings are omitted from these two GETs. Remaining nulls will be filled to SHADOW; until then a null stage still passes this gate

Three independent gates decide whether a finding appears on the customer read API. All must pass:

  1. Insight type visibility — the type must be external (internal types never surface to external integrators).
  2. insights_insight.rollout_state — the detector must not have been in SHADOW for the student's school when the finding fired (a null stage passes). The stage is frozen onto the finding when it is minted, read from the rollout policy pinned on the session: the school's own stage for that type when it has one, otherwise the type's catalog default, and SHADOW when the session carries no pinned policy. Shadow findings are stored for review and enforcement calibration but are not served on the customer read API or delivered as insight.* webhook events. Session notifications (GET /insights/1.0/sessions/{sessionId}/notifications) still return SHADOW findings — that path is in-session, not a customer dashboard read.
  3. insights_insight.outcome — the segment must be raised. A suppressed segment is one a detector produced and the platform then declined to raise; it is stored for audit and detector maturity. A segment is suppressed when its context predicate misses (for example the student was not in an assessment window, or an academic-integrity signal fired on a standard session), when the occurrence is shorter than the minimum occurrence duration configured for its type, or when a later event withdraws the finding (for example the student returned to an abandoned assessment inside the return horizon). Treat those reasons as an open list rather than an exhaustive one; more are added as detectors mature. A suppressed segment does not appear on these two GETs, does not emit insight.* webhooks, and is excluded from coaching analytics views.

Example Request

const sessionId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(`https://platform.timeback.com/insights/1.0/sessions/${sessionId}`, {
  headers: { Authorization: `Bearer ${accessToken}` },
});

const data = await response.json();

Mint a Session Share

Mint a parent-facing Vault share for a session you can already read, at send time.

POST /external-access/1.0/sessions/{sessionId}/shares

There is no request body. The response is 201 with:

Field Type Description
id string (uuid) Share id
url string (uri) Vault share URL of the form {vaultHost}/share/{id}. Store this URL. Do not assemble /sessions/{sessionId}.
expiresAt string (date-time) Instant the parent-facing link stops resolving, always 604800 seconds after the share was written

The caller must hold the https://timeback-platform.trilogy.com/external-access/scope/share.manage scope (not events.readonly) and an organization grant that covers the student (the granted organization and its descendants). A known session outside that grant is 403 and no share is created. An unknown or revoked session is 404.

Each call writes a new share. Nothing is deduplicated and no existing share is extended.

Path Parameters

Parameter Type Required Description
sessionId string Yes Caliper session id (URL-encoded if it is an IRI)

Example Request

const sessionId = encodeURIComponent(caliperSessionId);
const response = await fetch(`https://platform.timeback.com/external-access/1.0/sessions/${sessionId}/shares`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${accessToken}` },
});

const { url, expiresAt } = await response.json();

Get User Insights Overview

Retrieve aggregated overview data including trends and breakdowns for a time range.

GET /insights/1.0/users/{userId}/overview

Query Parameters

Parameter Type Required Description
applicationId uuid No Scope the whole overview to one learning app
startedAfter datetime Yes Lower bound for time window (inclusive)
startedBefore datetime Yes Upper bound for time window (exclusive)
targetTimezone string Yes IANA timezone for local-day bucketing

Omitting applicationId returns the whole-account overview, unchanged. Supplying it scopes every part of the response — summary, trend buckets, level breakdown and subtypes — to that learning app's sessions, using the same precedence merge as the unscoped call. An applicationId that is not a registered application is refused with 404, rather than silently returning the unscoped answer; a registered application the student simply did not use in the range returns the normal empty overview. Note that this differs from GET /insights/1.0/users/{userId}/sessions, which returns an empty list for an unknown application.

When summing per-app figures, be aware that time two apps share is counted once in the unscoped totals and once in each app's scoped totals: the sweep that resolves overlapping insights runs within a scope, not across scopes. Summing every app's waste therefore reconciles exactly with the unscoped total when the student's sessions in different apps do not overlap, and is an upper bound when they do. Every session belongs to exactly one application, so there is no unattributed remainder.

Example Request

const params = new URLSearchParams({
  startedAfter: '2025-01-01T00:00:00Z',
  startedBefore: '2025-01-08T00:00:00Z',
  targetTimezone: 'America/New_York',
});

const response = await fetch(`https://platform.timeback.com/insights/1.0/users/${userId}/overview?${params}`, {
  headers: { Authorization: `Bearer ${accessToken}` },
});

const data = await response.json();

Example Response

{
  "summary": {
    "totalDurationInSeconds": 36000,
    "totalWasteDurationInSeconds": 5400,
    "wastePercentage": 15,
    "biggestInsight": {
      "level": "OnDeviceDistractions",
      "durationInSeconds": 2400,
      "wastePercentage": 7
    }
  },
  "trend": {
    "buckets": [
      {
        "startedAtTime": "2025-01-01T00:00:00Z",
        "endedAtTime": "2025-01-02T00:00:00Z",
        "totalDurationInSeconds": 7200,
        "wasteDurationInSeconds": 1080,
        "wastePercentage": 15,
        "levels": [
          { "level": "EnvironmentalDistractions", "durationInSeconds": 300 },
          { "level": "OnDeviceDistractions", "durationInSeconds": 780 }
        ]
      }
    ]
  },
  "breakdown": {
    "levels": [
      {
        "level": "OnDeviceDistractions",
        "durationInSeconds": 2400,
        "wastePercentage": 7,
        "subtypes": [{ "insightType": "FocusLeftApp", "durationInSeconds": 2400 }]
      },
      {
        "level": "EnvironmentalDistractions",
        "durationInSeconds": 900,
        "wastePercentage": 3,
        "subtypes": [{ "insightType": "Socializing", "durationInSeconds": 900 }]
      }
    ]
  }
}

Insight Types

GET /insights/1.0/types is the authoritative list of insight types available to your credentials. See the API Reference for the full operation. This guide explains what the categories mean and how the types are used. It does not restate the catalog as a fixed list of slugs.

GET /insights/1.0/types returns the global active registry visible to the authenticated caller. Results do not vary by organization. External callers receive only visibility=external types; internal callers also receive active internal types. Locale message bundles and organization policy do not participate in this endpoint. Waste Meter consumers select entries with isWaste=true from the response. Every partner-visible type carries a published Insight Group slug in insightGroup (see GET /signals/1.0/insights/groups). Catalog reads are cached in-process for up to 10 minutes, so registry changes become visible across warm Lambda containers within that bound.

Each type also carries scope, audience and latency. scope says which sessions the type covers: Test window (proctored test time only), Learning (ordinary study) or All sessions. audience names who the type is written for: Student, Guide, AppOwner, Safety or Internal. latency says when an occurrence is expected to be available after it happens: Live, Session end or Next day. The three fields are null on types outside the catalog v1 vocabulary. External credentials receive seven categories: AcademicIntegrity, Observability, PhysicalEnvironment, OnlineEnvironment, LearningBehaviour, StudentAdvocacy and Journal. A deprecated type keeps its own category until its sunsetAt, so a type with deprecated: true can still carry a category outside that list. Safety and TimeLedger are internal and are not returned to partners. Read scope, audience and latency from the response rather than inferring them from the category.

The Categories

Each category groups types by the kind of insight. The catalog defines the active types and their session scope.

Category What it covers
AcademicIntegrity Integrity during tests and help during learning. Learning types include HelpFromPerson, HelpFromDevice, HelpFromApp and DeviceInView. Test-window types end in DuringTest.
Observability Camera, microphone and screen capture conditions that affect whether the session can be assessed.
PhysicalEnvironment Conditions around the student, such as Socializing or AwayFromSeat.
OnlineEnvironment Activity on the device outside the learning app, such as FocusLeftApp or NonLearningContent.
LearningBehaviour Learning actions such as starting, idling, leaving or skipping an assessment.
StudentAdvocacy Issues that affect the student's learning experience and need attention from the responsible adult or app owner.

Safety and TimeLedger are internal categories. Partners do not receive them. Journal carries narrative entries describing what happened in a session, as text rather than as wasted time.

Observability applies to both learning and test sessions. AcademicIntegrity includes test-window rules and help during learning. The catalog's scope field identifies where each type applies.

Select by category, then resolve the types from the catalog. Slugs in this guide are examples, not a list to compile into your product. GET /insights/1.0/types returns the active types visible to your credentials. See How Types Change.

Unfiltered Reads and Type Selection

With no category, type or insightGroups filter, insight reads return every insight type visible to the caller. Add a filter to narrow the results. Retired slugs are not valid type filter values. A type that has passed its sunset date is unknown to listing endpoints.

Params passed Types returned
?category=AcademicIntegrity,Observability,OnlineEnvironment,PhysicalEnvironment,LearningBehaviour,StudentAdvocacy Types in those categories that are visible to the caller
?category=PhysicalEnvironment Types in one category
?type=Socializing,NonLearningContent The selected types, if they are active and visible
?insightGroups=StayFocused,DistractionFreeSpace Types in those groups

Precedence: type (or insightTypes on session endpoints) > insightGroups > category. An isProctored filter limits sessions to the requested proctored status. It does not change the types returned.

The wasteDurationInSeconds and wastePercentage fields reflect the intersection of fetched types and types carrying isWaste = true. The category or type filter you pass determines which waste appears. Waste types belong to LearningBehaviour, PhysicalEnvironment and OnlineEnvironment. AcademicIntegrity, Observability and StudentAdvocacy types do not count as waste. These waste types are NeverStarted, Idling, AwayFromSeat, OtherDeviceUse, Socializing, NonLearningContent, FocusLeftApp, AbandonedAssessment and SkippedAssessment. NonLearningContent, Idling, Socializing and AwayFromSeat include rows merged from retired types. Use isWaste from the catalog as the source of truth. Two endpoints are exceptions. On org-level enriched sessions (GET /sessions), the fetched set decides both insightsSummary and the waste fields. The overview endpoint accepts no type or category parameter. It always spans every waste type. AcademicIntegrity and Observability insights do not appear in overview data because they are not waste types.

Org-level enriched sessions (GET /sessions)

insightTypes filters which sessions come back. enrichmentInsightTypes and enrichmentInsightCategories select what each returned session reports in insightsSummary and the fetched insight set — they do not themselves decide which sessions match insightTypes. Waste metrics are computed from that fetched set, so when minWastePercentage, maxWastePercentage, or the waste-duration bounds are also present, a waste-free enrichment set can empty or shrink the page even though enrichment never feeds the session-type filter.

Enrichment resolution:

  1. Either enrichment param present → the resolved enrichment set (categories expand to their client-visible type slugs; union with any enrichment type slugs; internal-only types dropped for external credentials).
  2. Else insightTypes present → those types (after visibility filtering).
  3. Else → every insight type visible to the caller.

Because the fetched set also drives wasteDurationInSeconds and wastePercentage, a caller that reads or filters by waste includes waste-carrying categories in its enrichment set. An enrichment set with no waste types returns rows with zero waste (and drops them under a min-waste filter).

Unknown enrichment type slugs return 400. Unknown categories contribute no types, so a request whose enrichment resolves to nothing returns its sessions with an empty insightsSummary. Internal-only types (Safety, Journal and TimeLedger) are dropped from enrichment rather than erroring; unlike the insightTypes filter, that visibility drop does not short-circuit to an empty page.

Reading the Catalog

Call the type catalog to discover which types your credentials can retrieve:

GET /insights/1.0/types

Example response (field shapes are complete; which types appear depends on your credentials and filters):

[
  {
    "id": "d66e396d-8842-404e-83f9-2881bea8f29d",
    "slug": "Socializing",
    "displayName": "Socializing",
    "category": "PhysicalEnvironment",
    "visibility": "external",
    "isContinuous": false,
    "precedenceLevel": 3,
    "isWaste": true,
    "description": "Socializing is when you're part of a conversation during a learning session.",
    "color": null,
    "triggersInSessionNotification": false,
    "detectionSources": ["webcam"],
    "insightGroup": "DistractionFreeSpace",
    "scope": null,
    "audience": null,
    "latency": null
  },
  {
    "id": "fcdeabd6-6a5e-486e-969e-dd7b477bccab",
    "slug": "NotFullScreenDuringTest",
    "displayName": "Not full screen during test",
    "category": "AcademicIntegrity",
    "visibility": "external",
    "isContinuous": false,
    "precedenceLevel": null,
    "isWaste": false,
    "description": null,
    "color": null,
    "triggersInSessionNotification": true,
    "detectionSources": ["client"],
    "insightGroup": "OnlyTheTest",
    "scope": null,
    "audience": null,
    "latency": null
  }
]

Insight Groups

GET /signals/1.0/insights/groups returns the published Insight Groups (the source of truth for the set). It requires the https://timeback-platform.trilogy.com/signals/scope/insights.readonly scope. Each entry includes slug, displayName, family, membershipRule, customerFacing, and insightTypeCount (how many insight types visible to your credentials belong to that group). Results do not vary by organization.

On type and insight responses (and webhook payloads), insightGroup is the group slug (or null), for example StayFocused. Display names are served only by GET /signals/1.0/insights/groups — do not compile a fixed slug list or parse slugs into labels locally.

Pass insightGroups (comma-separated slugs) on list endpoints to filter by group instead of category or type slug. On endpoints that return sessions (GET /users/{userId}/sessions, GET /sessions), a session is returned only if it carries at least one caller-visible insight of a member type, and the session's waste metrics scope to the filter. The exception is GET /users/{userId}/sessions: category on that endpoint scopes metrics only and never removes a session. On org enriched sessions, insightTypes takes precedence when both are set.

Type Metadata

Each insight type returned by GET /insights/1.0/types has the following properties:

Field Type Description
id string (uuid) Stable identifier for the type
slug string Unique PascalCase identifier (for example, Socializing)
displayName string Human-readable name
category string Category name. See The Categories
visibility string external or internal. The endpoint only returns types your credentials may see, so an external integrator observes only external
isContinuous boolean Whether this type supports continuous gap-based merging
precedenceLevel number | null Precedence for waste overlap resolution (1=highest, 5=lowest, null if not applicable)
isWaste boolean Whether this type counts toward waste calculations
description string | null Human-readable description for tooltips
color string | null Hex color for UI rendering (e.g., #FF5722)
triggersInSessionNotification boolean Whether instances of this type surface as in-session notifications to the student
detectionSources string[] Semantic medium(s) analyzed to mine this type: webcam, audio, screen, client, dom, network, app_events. Empty means no known medium dependency
insightGroup string | null Published Insight Group slug this type belongs to. Resolve display labels via GET /signals/1.0/insights/groups
deprecated boolean true when the type is no longer recommended for new integrations. Always present; false until a deprecation window starts
sunsetAt string | null RFC 3339 datetime with offset for when the type will leave the catalog. null until a removal date is committed

Each insight returned by user and session list endpoints carries the published insightGroup for its insightType.

How Types Change

Insight types can be added, renamed or retired. Retired types have passed their sunsetAt and are absent from the catalog. Insights already recorded remain readable under their catalog v1 type name.

Renamed types

These retired partner-visible registered types map to catalog v1 slugs. Existing insights are returned under the mapped v1 name.

Registered type Catalog v1 slug
AcademicIntegrityWorkAlone SocializingDuringTest
AcademicIntegrityNoDevices on a test DeviceInViewDuringTest
AcademicIntegrityNoDevices during learning DeviceInView
AcademicIntegrityStayInTheTest during a test window FocusLeftAppDuringTest
OnlineAppFocusSwitch FocusLeftApp
AcademicIntegrityOnlyTheTestOnScreen OtherAppOnScreenDuringTest
AcademicIntegrityFullScreenRequired NotFullScreenDuringTest
AcademicIntegrityOneDisplayOnly SecondDisplayDuringTest
AcademicIntegrityNoPaste PasteDuringTest
CheatingHelpFromAnotherPerson or ProctoringHelpFromAnotherPerson in a test window HelpFromPersonDuringTest
CheatingUnauthorizedDeviceUse or ProctoringUnauthorizedDeviceUse in a test window HelpFromDeviceDuringTest
CheatingUnauthorizedAppUse or ProctoringUnauthorizedAppUse in a test window HelpFromAppDuringTest
CheatingHelpFromAnotherPerson during learning HelpFromPerson
CheatingUnauthorizedDeviceUse during learning HelpFromDevice
CheatingUnauthorizedAppUse during learning HelpFromApp
ObservabilityFaceInView FaceNotVisible
PhysicalVerbalSocializing Socializing
OnlineOffAppAttention NonLearningContent
SkipAssessment SkippedAssessment
ObservabilityCameraPermissionOn CameraPermissionDenied
ObservabilityCameraWorking CameraNotWorking
ObservabilityCameraUncovered CameraCovered
ObservabilityMicrophoneWorking MicrophoneNotWorking
ObservabilityScreenRecordingOn ScreenNotRecorded

Build integrations that survive those changes:

  1. Read the catalog at runtime and cache it with a TTL — do not compile a fixed list of slugs into your product.
  2. Render displayName, description, and color from the catalog rather than from a local lookup table.
  3. Treat an unrecognized slug as displayable, never as an error.
  4. Do not write an exhaustive switch over slugs with a throwing default.
  5. Derive waste from isWaste rather than from a hard-coded slug list.

Published changes to the public API surface and to insight-type semantics are recorded in the Changelog.

Retired Insight Types

Retired types have passed their sunsetAt and are absent from the catalog. Their recorded insights remain readable under their current type names. Old slugs are not valid filter values on endpoints that validate types. Listing endpoints treat past-sunset types as unknown.

GET /insights/1.0/types remains authoritative for what your credentials can retrieve. See How Types Change for integration guidance and the Changelog for published updates.

Overview Levels

Overview data groups insights into six levels:

Level Description
StudentNotPresent Student away from seat or eyes off screen
FocusAndIntensity Reduced focus or intensity during learning (for example, idling)
EnvironmentalDistractions External distractions (eating, socializing)
OnDeviceDistractions Digital distractions (games, social media)
LearningAppBestPractices Learning behavior issues (skipping, shopping)
Unclassified Insight time that maps to no other level — render it rather than dropping it

Looking Up Application Information

Insights and sessions include an applicationId field that identifies which learning app the data came from. To map application IDs to application names and details, use the Applications API.

Get All Applications

GET /applications/1.0

This endpoint returns all applications. Each application's sourcedId is the applicationId you'll see in insights responses.

Required scope: https://purl.imsglobal.org/spec/lti/v1p3/scope/lti.readonly

This endpoint supports filtering, pagination, sorting, and field selection. See OneRoster API Conventions for details.

Example Request

const response = await fetch('https://platform.timeback.com/applications/1.0', {
  headers: { Authorization: `Bearer ${accessToken}` },
});

const data = await response.json();

Example Response

{
  "applications": [
    {
      "sourcedId": "app-uuid",
      "name": "Math Learning Suite",
      "description": "Comprehensive math learning platform",
      "logoUrl": "https://example.com/logo.png",
      "applicationType": "learning_app",
      "wasteMeter": "on",
      "proctoringMode": "off"
    }
  ],
  "offset": 0,
  "limit": 10,
  "total": 1
}

Building an Application Lookup Map

Cache application information to efficiently display app names in your dashboards:

interface ApplicationInfo {
  name: string;
  logoUrl: string;
}

async function buildApplicationLookup(accessToken: string): Promise<Map<string, ApplicationInfo>> {
  const response = await fetch('https://platform.timeback.com/applications/1.0?limit=100', {
    headers: { Authorization: `Bearer ${accessToken}` },
  });

  const data = await response.json();
  const applicationMap = new Map<string, ApplicationInfo>();

  for (const app of data.applications) {
    applicationMap.set(app.sourcedId, {
      name: app.name,
      logoUrl: app.logoUrl,
    });
  }

  return applicationMap;
}

// Usage: Display application name for a session
const applicationLookup = await buildApplicationLookup(accessToken);
const session = sessions[0];
const applicationInfo = applicationLookup.get(session.applicationId);
console.log(`Session in: ${applicationInfo?.name}`); // "Session in: Math Learning Suite"

Common Use Cases

Building a Student Dashboard

Use the overview endpoint to show weekly trends:

async function getWeeklyOverview(userId: string, timezone: string): Promise<Overview> {
  const now = new Date();
  const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);

  const params = new URLSearchParams({
    startedAfter: weekAgo.toISOString(),
    startedBefore: now.toISOString(),
    targetTimezone: timezone,
  });

  const response = await fetch(`https://platform.timeback.com/insights/1.0/users/${userId}/overview?${params}`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });

  return response.json();
}

Listing Recent Sessions with Waste Stats

async function getRecentSessions(userId: string): Promise<Session[]> {
  const response = await fetch(`https://platform.timeback.com/insights/1.0/users/${userId}/sessions?limit=10`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });

  const data = await response.json();
  return data.sessions;
}

Fetching All Insights for a Session

The unfiltered read returns every insight type visible to your credentials. The waste fields still count only types marked by isWaste.

async function getAllInsights(sessionId: string): Promise<Insights> {
  const response = await fetch(`https://platform.timeback.com/insights/1.0/sessions/${sessionId}`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  return response.json();
}

Drilling into Session Details

async function getSessionDetails(sessionId: string): Promise<Insights> {
  const response = await fetch(`https://platform.timeback.com/insights/1.0/sessions/${sessionId}`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });

  return response.json();
}

Error Handling

The API returns standard HTTP status codes:

Status Description
200 Success
400 Bad request (invalid parameters)
401 Unauthorized (missing or invalid token)
403 Forbidden (insufficient scope)
404 Not found (session or share doesn't exist)
500 Internal server error

Error responses include details:

{
  "error": "Bad request",
  "message": "Invalid UUID format for userId"
}

Level 3: Caliper Events

Insights are generated from Caliper events. Implement Level 3 first to start generating insights.

Session Management

Understand session lifecycle, auto-attach vs explicit sessions, heartbeat, and session metadata.

Session Integrity Reports

Store and read a per-session integrity report.

Session Adjustments

Read a session's adjusted time and adjusted XP (preview).

Authentication

Obtain OAuth client credentials and access tokens required for the Insights API.

OneRoster API Conventions

Learn about filtering, pagination, sorting, and field selection for the Applications API.