Level 4: Insights API

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.

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 four 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
GET /insights/1.0/users/{userId}/overview Get aggregated trend and breakdown data

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

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 Filter insights after this timestamp (inclusive)
before datetime No Filter insights before this timestamp (inclusive)
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 insight type slugs to filter by. Overrides category and default.
category string No Comma-separated categories (e.g., Engagement,Proctoring,Cheating). Overrides default.

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": "Idling",
      "reason": "No activity detected for extended period",
      "startedAtTime": "2025-01-15T09:15:00Z",
      "endedAtTime": "2025-01-15T09:20:00Z",
      "durationInSeconds": 300,
      "version": "v1"
    }
  ],
  "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., Engagement,Proctoring,Cheating). Overrides default.
isProctored boolean No Filter by proctored status. Also selects proctoring types when category is not provided.
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. Also selects proctoring types when category is not provided.
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 insight type slugs to filter by. Overrides category and default.
category string No Comma-separated categories (e.g., Engagement,Proctoring,Cheating). Overrides default.

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();

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
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

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": "StudentNotPresent", "durationInSeconds": 300 },
          { "level": "OnDeviceDistractions", "durationInSeconds": 780 }
        ]
      }
    ]
  },
  "breakdown": {
    "levels": [
      {
        "level": "OnDeviceDistractions",
        "durationInSeconds": 2400,
        "wastePercentage": 7,
        "subtypes": [
          { "insightType": "Game", "durationInSeconds": 1200 },
          { "insightType": "NonLearningContent", "durationInSeconds": 1200 }
        ]
      }
    ]
  }
}

Insight Types

TimeBack generates the following insight types from student activity. Use GET /insights/1.0/types to discover all available types and their metadata dynamically.

Default Behavior and Type Selection

By default, endpoints return Engagement-category insight types only. Use the category or type query params to include additional categories:

Params passed Types returned Example use case
(none) Engagement-category types only Default (backward compat)
?category=Engagement Engagement-category types only Engagement-only view
?category=Engagement,Proctoring,Cheating All three categories Combined insights view
?category=Cheating Cheating types only Cheating-specific view
?isProctored=true Proctoring types only (legacy) Proctoring tab
?type=TopicShopping,Idling Exact slugs only Custom type selection

Precedence: type > category > default. The wasteDurationInSeconds and wastePercentage fields reflect the intersection of fetched types and isWaste = true types. On student-level endpoints using the default filter, waste metrics cover Engagement-category types only. On org-level enriched sessions (GET /sessions), waste metrics cover all isWaste = true types (including proctoring and cheating). Use ?category=Engagement,Proctoring,Cheating on student-level endpoints to align waste calculations with the org-level view.

Type Metadata

Each insight type has the following properties (available via GET /insights/1.0/types):

Field Type Description
slug string Unique identifier (e.g., AwayFromSeat)
displayName string Human-readable name
category string Category: Engagement, Proctoring, Cheating, Safety, Stability, Journal
isWaste boolean Whether this type counts toward waste calculations
isContinuous boolean Whether this type supports gap-based merging
precedenceLevel number | null Precedence for waste overlap resolution (1-5, null if not applicable)
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

Engagement Insights (Time Off-Task)

Type Description
AwayFromSeat Student not detected at their desk
EyesOffScreen Student looking away from screen
Idling No activity for extended period
NonLearningContent Browsing non-educational content
Socializing Social media or messaging activity
IgnoringHelp Student dismissed or ignored coaching suggestions
SkipAssessment Student skipped assessment questions
AbandonedAssessment Student left assessment incomplete
TopicShopping Rapidly switching between topics without progress

Proctoring Insights

Proctoring types have isWaste = true and category = Proctoring. Their durations count toward waste calculations.

Type Description
HelpFromAnotherPerson Receiving unauthorized help (legacy, unprefixed)
UnauthorizedAppUse Using applications not allowed during session (legacy)
UnauthorizedDeviceUse Using an unauthorized device during session (legacy)
ProctoringHelpFromAnotherPerson Help from another person detected during proctored session
ProctoringUnauthorizedAppUse Unauthorized app usage detected during proctored session
ProctoringUnauthorizedDeviceUse Unauthorized device usage during proctored session

Cheating Insights

Cheating types have isWaste = true and category = Cheating. They use different detection thresholds than proctoring types (e.g., "help from another person" only counts when a direct answer is provided).

Type Description
CheatingHelpFromAnotherPerson Help from another person detected during a non-proctored session
CheatingUnauthorizedAppUse Unauthorized app usage detected during a non-proctored session
CheatingUnauthorizedDeviceUse Unauthorized device usage during a non-proctored session

Safety Insights

Type Description
BullyingOrHarassment Bullying or harassment detected
EmbarrassingOrDefaming Embarrassing or defaming content
MentalHealthDistress Signs of mental health distress
PornographyOrSexualContent Pornographic or sexual content
SelfHarmOrSuicide Self-harm or suicide-related content
SubstanceAbuse Substance abuse detected
ViolenceOrAbuse Violence or abuse detected
WebcamNudity Nudity detected via webcam

Overview Levels

Overview data groups insights into four levels:

Level Description
StudentNotPresent Student away from seat or eyes off screen
EnvironmentalDistractions External distractions (eating, socializing)
OnDeviceDistractions Digital distractions (games, social media)
LearningAppBestPractices Learning behavior issues (skipping, shopping)

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 Insight Categories for a Session

// Get waste + proctoring + cheating insights in one call
async function getAllInsights(sessionId: string): Promise<Insights> {
  const response = await fetch(
    `https://platform.timeback.com/insights/1.0/sessions/${sessionId}?category=Engagement,Proctoring,Cheating`,
    { 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 (user 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.

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.