Session Time Adjustments
Store one time adjustment per session, then read it back by session.
Preview: a session's adjusted time is computed by a port of the StudyFilm feed's daily arithmetic. Values may change until a production parity check against the feed is accepted. Until then the feed's daily reports stay the reference for schools.
Note: This guide covers the integration flow. See the API Reference for complete endpoint documentation, all parameters, and response schemas.
What a time adjustment is
A session's base time is split into seven classes. Wasted time and confirmed academic-integrity time are deducted. What remains is the adjusted time:
adjustedSeconds=baseSeconds-deductedSeconds.secondsByClassholds the seven classes, and their values sum tobaseSeconds.suspectedSecondscounts time under suspected, unconfirmed integrity spans. It is reported, never deducted.
Values are whole seconds. Divide by 60 for minutes.
The platform keeps one record per session. A later write for the same session replaces it.
Prerequisites
-
Register your App and obtain OAuth credentials. See Level 0: Register Your App and Authentication.
-
Request the Caliper event scopes for your M2M client:
- Write:
https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.write - Read:
https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.readonly
- Write:
-
Own the session's organization. A caller without organization access receives
403.
Classes
| Class | Deducted |
|---|---|
PRODUCTIVE |
No |
LOW_PRODUCTIVITY |
No |
SCHOOL |
No |
SYSTEM_FAILURE |
No |
WASTED |
Yes |
INSINCERE |
Yes |
UNOBSERVED |
No |
INSINCERE is time covered by a confirmed academic-integrity violation. UNOBSERVED is time where nothing could be observed. The other classes are the StudyFilm window labels.
The producer decides which classes it deducts under its ruleVersion. deductedSeconds is the producer's total.
Integrity status
| Value | Meaning |
|---|---|
CONFIRMED |
At least one confirmed academic-integrity violation. |
SUSPECT |
Suspected, unconfirmed violations only. |
CLEAN |
Reviewed and clean. |
NOT_ESCALATED |
Screening found nothing to review. |
UNJUDGED |
Review did not finish. Nothing is deducted for integrity. |
Fields
| Field | Meaning |
|---|---|
baseSeconds |
Session duration the producer classified. |
adjustedSeconds |
baseSeconds minus deductedSeconds. |
deductedSeconds |
Seconds removed from the base. |
suspectedSeconds |
Seconds under suspected integrity spans. Never deducted. |
secondsByClass |
All seven classes, each a non-negative integer, summing to baseSeconds. |
integrityStatus |
One of the values above. |
ruleVersion |
Version of the rules that produced the record, 1 to 64 characters. |
source |
Producer id, 1 to 64 characters. |
computedAt |
Instant the producer computed the record, RFC 3339. |
id, dateCreated, dateLastModified |
Server-assigned. |
Step 1: Store a time adjustment
POST /session/1.0/sessions/{sessionId}/time-adjustment
Requires events.write. sessionId is the Caliper session id. A later write for the same session replaces the record.
Request body:
{
"baseSeconds": 1200,
"adjustedSeconds": 960,
"deductedSeconds": 240,
"suspectedSeconds": 0,
"secondsByClass": {
"PRODUCTIVE": 840,
"LOW_PRODUCTIVITY": 0,
"SCHOOL": 120,
"SYSTEM_FAILURE": 0,
"WASTED": 240,
"INSINCERE": 0,
"UNOBSERVED": 0
},
"integrityStatus": "NOT_ESCALATED",
"ruleVersion": "feed-f215c05",
"source": "window-graph",
"computedAt": "2026-03-10T09:35:00.000Z"
}
Response 200:
{
"sessionId": "00000000-0000-4000-8000-000000000001",
"timeAdjustment": {
"id": "11111111-1111-4111-8111-111111111111",
"baseSeconds": 1200,
"adjustedSeconds": 960,
"deductedSeconds": 240,
"suspectedSeconds": 0,
"secondsByClass": {
"PRODUCTIVE": 840,
"LOW_PRODUCTIVITY": 0,
"SCHOOL": 120,
"SYSTEM_FAILURE": 0,
"WASTED": 240,
"INSINCERE": 0,
"UNOBSERVED": 0
},
"integrityStatus": "NOT_ESCALATED",
"ruleVersion": "feed-f215c05",
"source": "window-graph",
"computedAt": "2026-03-10T09:35:00.000Z",
"dateCreated": "2026-03-10T09:35:01.000Z",
"dateLastModified": "2026-03-10T09:35:01.000Z"
}
}
Step 2: Read one session
GET /session/1.0/sessions/{sessionId}/time-adjustment
Requires events.readonly. A stored record returns the same shape as the write response.
A known session with no record yet returns 200 with timeAdjustment: null:
{
"sessionId": "00000000-0000-4000-8000-000000000001",
"timeAdjustment": null
}
That means no adjustment has been computed yet. Use the session's raw duration until one arrives. An unknown or revoked session returns 404.
Errors
| Status | Cause |
|---|---|
400 |
secondsByClass does not sum to baseSeconds, is missing a class, or has an extra one. adjustedSeconds is not baseSeconds minus deductedSeconds. A value is negative or not an integer. |
401 |
The token is missing, expired, or lacks the scope the endpoint needs. |
403 |
The caller lacks organization access, or a student tries to write a record. |
404 |
The session is unknown or revoked. |
