Level 4: Insights API
Deprecated: The
/insights/1.0read 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:
- Implement Level 3 (Caliper Events)—insights are generated from Caliper events you send
- Request OAuth credentials with the
events.readonlyscope (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:
- Insight type
visibility— the type must beexternal(internal types never surface to external integrators). insights_insight.rollout_state— the detector must not have been inSHADOWfor 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, andSHADOWwhen 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 asinsight.*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.insights_insight.outcome— the segment must beraised. Asuppressedsegment 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 emitinsight.*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:
- 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).
- Else
insightTypespresent → those types (after visibility filtering). - 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:
- Read the catalog at runtime and cache it with a TTL — do not compile a fixed list of slugs into your product.
- Render
displayName,description, andcolorfrom the catalog rather than from a local lookup table. - Treat an unrecognized slug as displayable, never as an error.
- Do not write an exhaustive
switchover slugs with a throwing default. - Derive waste from
isWasterather 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"
}
Related Docs
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.
