Changelog

This page records partner-visible changes: operations in the published API Reference, documented behaviour in the published guides, and insight-type semantics. Internal changes with no external effect are not listed here.

2026.10.27.1

  • Signals actions (listSessionActions GET /signals/1.0/sessions/{sessionId}/actions): each action now carries resolvedAt, with the same meaning as on listUserActions. For a proctoring action such as BlockTest, it is when the action happened, including actions recorded through PUT /signals/1.0/actions/{actionId}, which leave startedAt null. See Signals Feedback. Non-breaking.

2026.10.26.3

  • Signals insights (listSessionInsights GET /signals/1.0/sessions/{sessionId}/insights): insights still under internal evaluation are no longer listed or counted in total. upsertInsightFeedback PUT /signals/1.0/insights/{insightId}/feedback returns 404 for such an insight, and listSessionFeedback GET /signals/1.0/sessions/{sessionId}/feedback leaves out feedback on it. Every insight you can list remains one you can rate. See Signals Feedback. Non-breaking.

2026.10.26.2

  • Signals actions (listSessionActions GET /signals/1.0/sessions/{sessionId}/actions): each action now carries type (for example BlockTest or Warn), initiatorUserId (the person who started the action, such as a guide, or null when no person did), and dateCreated (when the platform received the action), with the same meaning as on listUserActions. Keep only the action types you handle by reading type. See Signals Feedback. Non-breaking.

2026.10.26.1

  • Webhooks: subscribe to action.created to receive every Signals action created for a student, whatever its type, with { actionId, type, userId, caliperSessionId, occurredAt }. Filter on data.type (PascalCase, e.g. BlockTest, PreventTestStart). caliperSessionId is null when the action has no session. Every subscription that lists proctoring_enforcement.recorded now also lists action.created; proctoring_enforcement.recorded keeps firing unchanged. See Webhooks. Non-breaking.
  • Analytics schema: proctoring_enforcement_actions now serves the Signals actions PreventTestStart, BlockTest, UnblockTest, and EndTest. action_type carries those names instead of prevented_start, blocked_mid_test, unblocked_mid_test, and exited_mid_test; the warning, forced_end, and locked_out_restart kinds are no longer served. session_id is now nullable and is null on every PreventTestStart. The reason_category, insight_type, insight_group, and rule_visibility columns are removed. Breaking for queries that filter on the old action_type values or read the removed columns.

2026.10.23.1

  • getOrgSessions GET /insights/1.0/sessions: sessions no longer carry enforcementSummary (autoStopped, warningCount, notificationCount). Stop reading it. The enforcement filter is unchanged. Breaking for callers that read enforcementSummary.

2026.10.22.1

  • Content ingestion (packages uploaded via getIngestUploadUrl POST /content/1.0/ingest/upload-url): new optional packageBuiltAt request field (ISO 8601 date-time). Importing a package no longer restores an item deleted with DELETE /content/1.0/items/{itemId}, unless packageBuiltAt is later than the deletion: send the time you built the package, and a package built before the delete cannot revive the item. Without packageBuiltAt, a deleted item stays deleted. See Content Ingestion. Non-breaking for callers that never re-import a deleted item; callers that relied on a re-import restoring a deleted item must now send packageBuiltAt.
  • upsertIssuedAction PUT /signals/1.0/actions/{actionId}: state also accepts COMPLETED, for an action type issued already completed (RedirectOnLaunch is not; sending COMPLETED for it returns 400). A new optional occurredAt sets resolvedAt when the PUT sets state, so a cancel through the PUT can say when it happened, as the state endpoint already allows; it defaults to the server time, and sending it without state returns 400. See Signals Actions. Non-breaking.

2026.10.21.1

  • Session integrity reports: use storeSessionIntegrityReport POST /session/1.0/sessions/{sessionId}/integrity-report, readSessionIntegrityReport GET /session/1.0/sessions/{sessionId}/integrity-report, and listIntegrityReports GET /session/1.0/integrity-reports. They take the same requests, return the same responses, and require the same events.write / events.readonly scopes as upsertSessionIntegrityReport POST /insights/1.0/sessions/{sessionId}/integrity-report, getSessionIntegrityReport GET /insights/1.0/sessions/{sessionId}/integrity-report, and getIntegrityReportRollup GET /insights/1.0/integrity-reports. Switch your calls to the /session/1.0 paths. The /insights/1.0 paths are deprecated, now return Deprecation and Sunset: Fri, 23 Oct 2026 23:59:59 GMT response headers, and will be removed after 2026-10-23 23:59 UTC. Reports already stored read the same on both paths, and the insights_session_integrity_report.ready webhook event is unchanged. See Session Integrity Reports. Non-breaking.

2026.10.20.5

  • listUserOpenActions is renamed listUserActions, and GET /signals/1.0/users/{userId}/actions now returns every action for the student by default, in any state and window, each with its expired flag. New optional filters: state (comma-separated OPEN, SEEN, COMPLETED, SKIPPED, CANCELLED), inWindow=true (only actions inside their delivery window), and inclusive createdAfter / createdBefore. Call with state=OPEN,SEEN&inWindow=true to get the previous default. See Signals Actions. Breaking for callers relying on the previous default.

2026.10.20.4

  • Issued Signals actions are now in the published API Reference: upsertIssuedAction PUT /signals/1.0/actions/{actionId}, getIssuedAction GET /signals/1.0/actions/{actionId}, listIssuedActions GET /signals/1.0/actions, listUserOpenActions GET /signals/1.0/users/{userId}/actions, and updateActionState PUT /signals/1.0/actions/{actionId}/state. A client with the https://timeback-platform.trilogy.com/signals/scope/actions.write scope and the type's permission on its tenant (for RedirectOnLaunch, signals:issue_action:redirect_on_launch) can issue an action for a student under an id it generates, cancel it, and read back its state, seenAt, resolvedAt and derived expired flag. Issuer calls take the client's own token; a person's token, or one delegated to a person, is refused with 403. See Signals Actions. Non-breaking.

2026.10.20.3

  • Share links: POST /insights/1.0/sessions/{sessionId}/shares is removed. Use POST /external-access/1.0/sessions/{sessionId}/shares with the share.manage scope. Share URLs you already delivered keep working until they expire. Breaking for callers of the old path.

2026.10.20.2

  • Signals actions (listSessionActions GET /signals/1.0/sessions/{sessionId}/actions): the student an action targets is now targetUserId; userId is no longer returned. state can also be COMPLETED, SKIPPED or CANCELLED, and message, startedAt and endedAt can be null for an action no insight raised. See Signals Feedback. Breaking: read targetUserId instead of userId.

2026.10.20.1

  • Signals actions (listSessionActions GET /signals/1.0/sessions/{sessionId}/actions): each action now carries userId, the student it targets, and tbSessionId, the session it was raised in. tbSessionId can be null for an action raised outside a session; read the student from userId rather than through the session. See Signals Feedback. Non-breaking.

2026.10.19.2

  • Share links: use POST /external-access/1.0/sessions/{sessionId}/shares to mint a parent-facing session share. It takes the same request and returns the same response (id, url, expiresAt), with the same organization-grant rules. It requires the https://timeback-platform.trilogy.com/external-access/scope/share.manage scope instead of events.readonly: request that scope on your client credentials token. POST /insights/1.0/sessions/{sessionId}/shares is deprecated and will be removed. Share URLs you already delivered keep working until they expire. Non-breaking.

2026.10.19.1

  • Signals feedback is now in the published API Reference: listSessionInsights GET /signals/1.0/sessions/{sessionId}/insights, upsertInsightFeedback PUT /signals/1.0/insights/{insightId}/feedback, listSessionFeedback GET /signals/1.0/sessions/{sessionId}/feedback, listSessionActions GET /signals/1.0/sessions/{sessionId}/actions, upsertActionFeedback PUT /signals/1.0/actions/{actionId}/feedback, and getActionFeedback GET /signals/1.0/actions/{actionId}/feedback. A school credential with the Signals scopes can rate a session's insights and respond to its actions on behalf of a staff member, then read those ratings and responses back. See Signals Feedback. Non-breaking.

2026.10.14.1

  • Sessions (POST /caliper/v1p2, and the session times returned by GET /insights/1.0/sessions/{sessionId}): a session's startedAtTime and endedAtTime no longer move past the time the platform processed the event that set them. An event whose eventTime runs ahead of the platform clock is still stored with that eventTime, and a LoggedOut stamped ahead still completes the session, but the session's times stop at the processing time. An event naming a completed session is now not stored only when it is stamped after both the session's endedAtTime and its own LoggedOut or TimedOut eventTime. See Session Management. Non-breaking.

2026.10.13.5

  • Outbound webhooks (createWebhookSubscription POST /webhooks/1.0/{sourcedApplicationId}/subscriptions and updateWebhookSubscription PUT on /webhooks/1.0/{sourcedApplicationId}/subscriptions/{subscriptionId}): a subscription can list insight.created, insight.updated, and insight.deleted again. An unrecognized event type is still rejected with 400. Non-breaking.

2026.10.13.4

  • Rostering, applications, gradebook, and CLR credentials list operations: sort=sourcedId sorts by the primary key instead of returning a database error. On those and the CASE list operations, a sort or filter identifier that names no field of the resource, or that names password, returns HTTP 400 instead of a database error or a silent match; a sort error reports invalid_sort_field on sort. A database column name such as date_created, or a field name in another letter case such as roles.ROLE, used in place of the field name also returns 400. Every field and relation path that resolved before, such as filter=roles.org.sourcedId or filter=class.school.sourcedId, is unchanged. Whitespace other than a space outside a quoted filter value, or text after a complete filter condition, returns 400. Breaking for callers that send any of these identifiers or filter shapes.

2026.10.13.3

  • Getting access: the request section no longer carries the approver-facing approval-queue link. Requesters still start at the request page. Non-breaking.

2026.10.13.2

  • Student config (GET and PUT /rostering/1.0/students/{sourcedId}/config): the body now includes secondCameraRecordLearningAppIds and secondCameraEnforceLearningAppIds, lists of learning-app ids where that student's second camera records, or where the test does not start until the second camera passes its check. Ids in a list must be unique and non-empty, and each must be a learning app with proctoring mode on. Omitted lists read as empty. Non-breaking.

2026.10.13.1

  • Getting access: requesters start at the access console request page. Campus reader and an automation key are approved right away when the roster already lists you as teacher, principal, administrator, or admission coordinator at every school you pick, or at a parent organization above it. Otherwise someone reviews the request. Network reader and write or family-contact access are reviewed by the platform team. An approved automation key or write key is collected from a one-time claim link that requires the requester's Google sign-in. Third-party app registration stays on Level 0. Work that page does not cover still goes to Alpha EDU Support. Reporting MCP connect for Claude and ChatGPT, including the URL the request page prints after approval, uses https://platform.timeback.com/mcps/analytics (OAuth). The analytics introduction no longer asks readers to email timeback@trilogy.com. The published API credential text points at Getting access. Non-breaking.

2026.10.12.1

  • Org config (GET and PUT /rostering/1.0/orgs/{sourcedId}/config): signalsActionRules no longer accepts absent-during-assessment-daily, device-visible-during-assessment, or left-test-seeking. academic-integrity-full-screen-required is unchanged. Breaking for callers that write the removed keys.
  • Org config (GET and PUT /rostering/1.0/orgs/{sourcedId}/config): signalsSanctionedDestinations is removed. Stored lists are deleted. Breaking for callers that read or write that key.
  • Caliper ingest (POST /caliper/v1p2): AbsentFromSeat, AnotherWindowVisible, TestFocusLost, FullScreenExited, Conversation, and DeviceVisible are no longer registered. The event rejects with the existing unregistered-type error. WindowStateSnapshot, MediaStateSnapshot, and InputEvent stay accepted. Breaking for producers of the removed types.
  • Caliper ingest (POST /caliper/v1p2): an ObservationEvent whose object type is CaliperObservation is now rejected with 400. Send Observation. See Supported Entities. Breaking for producers still sending CaliperObservation.
  • Content ingestion (packages uploaded via getIngestUploadUrl POST /content/1.0/ingest/upload-url): re-importing a Common Cartridge package now unlinks an item or sub-container from a package container when the manifest no longer lists it under that container. The item is not deleted, and its links to containers outside the package are unchanged. See Content Ingestion. Non-breaking.

2026.10.09.2

  • Caliper ingest (POST /caliper/v1p2): the ObservationEvent object type is now Observation. CaliperObservation is still accepted and stored as Observation. See Supported Entities. Non-breaking.

2026.10.09.1

  • Org session list (getOrgSessions GET /insights/1.0/sessions): a caller can opt into listing resolved learning sessions whose integrity report carries an unauthorized-help tag, using unauthorizedTags or integrityTags, and optionally reviewState. Matching rows include integrityTags, noteCount, and review, and the page includes integrityTagCounts. Calls that omit those parameters keep the previous query and response. Non-breaking.
  • Session insights (GET /insights/1.0/sessions/{sessionId}) and insight.created webhooks now return and deliver AcademicIntegrityNoDevices occurrences on non-proctored learning sessions, where every AcademicIntegrity occurrence on such a session was previously suppressed. A student still receives no notification, warning or enforcement from an AcademicIntegrityNoDevices occurrence on a learning session, and every other AcademicIntegrity type still applies to proctored sessions only. See Insight Types. Non-breaking.

2026.10.08.1

  • Session integrity reports (upsertSessionIntegrityReport POST /insights/1.0/sessions/{sessionId}/integrity-report, getSessionIntegrityReport GET on that path, getIntegrityReportRollup GET /insights/1.0/integrity-reports): each session object now includes required sessionMode, learning or proctored. The writer stamps it from the session. The producer does not send it. Non-breaking.

2026.10.07.4

  • Session integrity reports: upsertSessionIntegrityVerdict POST /insights/1.0/sessions/{sessionId}/integrity-verdict, getSessionIntegrityVerdict GET on that path, and getIntegrityVerdictRollup GET /insights/1.0/integrity-verdicts are now upsertSessionIntegrityReport, getSessionIntegrityReport, and getIntegrityReportRollup on POST and GET /insights/1.0/sessions/{sessionId}/integrity-report and GET /insights/1.0/integrity-reports. See Session Integrity Reports. Breaking for callers of the previous paths.
  • Outbound webhooks: the event type insights_session_integrity_verdict.ready is now insights_session_integrity_report.ready, and the documented refetch path is GET /insights/1.0/sessions/{sessionId}/integrity-report instead of GET /insights/1.0/sessions/{sessionId}/integrity-verdict. The payload is still { sessionId }. Breaking for subscribers filtering on the old event type.

2026.10.07.3

  • Caliper ingest (POST /caliper/v1p2): a SystemIdentifier inside otherIdentifiers, and an LtiSession in session or federatedSession, no longer rejects the envelope. Neither value is stored as a Caliper entity. Non-breaking.

2026.10.07.2

  • Session integrity verdicts (upsertSessionIntegrityVerdict POST /insights/1.0/sessions/{sessionId}/integrity-verdict, getSessionIntegrityVerdict GET on that path, getIntegrityVerdictRollup GET /insights/1.0/integrity-verdicts) are now in the published API Reference. A caller with events.write can store one verdict per session. A caller with events.readonly can read that verdict, including a session that has no row yet, or a test-level roll-up computed over the named sessions. See Session Integrity Verdicts. Non-breaking.

2026.10.07.1

  • Outbound webhooks: a subscriber can receive insights_session_integrity_verdict.ready when a session integrity verdict becomes readable through GET /insights/1.0/sessions/{sessionId}/integrity-verdict, including when a later judgement replaces the stored one, then refetch the verdict by sessionId. The event carries only sessionId. Non-breaking.

2026.10.06.5

  • Caliper ingest (POST /caliper/v1p2): a DigitalResource learningObjectives value, as a full object or a bare IRI, no longer rejects the envelope and is not stored. Non-breaking.

2026.10.06.4

  • Caliper ingest (POST /caliper/v1p2): once a session is closed by LoggedOut or TimedOut, a later event that names the session no longer moves its startedAtTime or endedAtTime. An event that names a closed session and is stamped after its endedAtTime is not stored and is not retried; events stamped at or before it are still stored and linked. See Session Management. Breaking for producers that send events naming a session after its LoggedOut or TimedOut.

2026.10.06.3

  • Student config (GET and PUT /rostering/1.0/students/{sourcedId}/config, GET /rostering/1.0/students/{sourcedId}/config/change-requests/latest, GET /rostering/1.0/config-change-requests) now also accepts https://timeback-platform.trilogy.com/student-config/scope/student-config.readonly on the three reads and https://timeback-platform.trilogy.com/student-config/scope/student-config.write on the write. The roster scopes those operations already required still authorize them. Non-breaking.

2026.10.06.2

  • Outbound webhooks: a subscriber can receive xp.created, xp.updated, and xp.deleted when an XP ledger entry becomes visible, its value changes, or it leaves GET /xp/1.0/users/{userId}/entries, then refetch the entry by ledgerEntryId. Non-breaking.

2026.10.06.1

  • Caliper ingest (POST /caliper/v1p2): an entity type the platform does not ingest rejects the whole envelope with HTTP 400 and the description The [Type1, Type2] entity types are not supported. target, referrer, group, membership, extensions, and attributes are outside the check. The accepted types are listed in Supported Entities. Breaking.

2026.10.05.3

  • Insights session summaries (GET /insights/1.0/users/{userId}/sessions, org session listings): a sitting in an app that declares exactly one subject now carries that subject from the moment the sitting is created, recorded or not. subject null means the app does not declare exactly one subject and the sitting has not been through a vision-end write. Non-breaking.

2026.10.05.2

  • Org config (GET and PUT /rostering/1.0/orgs/{sourcedId}/config): signalsActionRules accepts a fourth template key, academic-integrity-full-screen-required, with { enforced?, message? }. enforced defaults to false, so an organization must set it true before this action mints. Non-breaking.

2026.10.05.1

  • Org config (GET and PUT /rostering/1.0/orgs/{sourcedId}/config): signalsActionRules is now a map of template key to { minimumInsightCount?, windowHours?, message? }, not an array of full rules. Insight types and action version stay on the template and are rejected on write. GET returns the nearest stored map, not the effective rules: a field missing from that map can still apply from an ancestor when an action is evaluated. Breaking for callers that read or write the previous array shape.

2026.10.01.1

  • External graders guide: POST /content/1.0/grade waits up to 45 seconds for a bound grader (was 20). The caller may wait up to 60 seconds. A grader timeout or unusable grader result is HTTP 200 with gradingProgress: Failed and scoreGiven: null, not HTTP 504 or 502. Invalid request bodies still return 4xx before any response body. A platform error after the response has started streaming is also HTTP 200 with gradingProgress: Failed. See External Graders. Breaking for callers that treated HTTP 504 or 502 as the timeout or unusable-grader signal.
  • Insights session summaries (GET /insights/1.0/users/{userId}/sessions, GET /insights/1.0/sessions/{sessionId}/insights, org session listings): subjectSource may now be assessment, meaning the subject came from the sitting's Alpha Test assessments (gradebook line-item subject, or the assessment name when that field is missing). Non-breaking.

2026.09.30.1

  • External graders guide: POST /content/1.0/grade accepts itemIdentifier as uuid- plus a content-item id of at most 36 characters, or as the bare content-item id. Non-breaking.
  • Insights session summaries (GET /insights/1.0/users/{userId}/sessions, GET /insights/1.0/sessions/{sessionId}/insights, org session listings): subjectSource may now be activity, meaning the subject came from curriculum activity the parser recorded as AssignableEvent Completed in that session. Non-breaking.
  • Session enforcement state (GET /insights/1.0/sessions/{sessionId}/enforcement-state): on lockout, triggeringSessionId is always null. Key on triggeringCaliperSessionId. Non-breaking.

2026.09.29.1

  • Webhooks guide: student.config.updated is now in the event catalog. It fires when a student's config row set changes through PUT /rostering/1.0/students/{sourcedId}/config or an approved parent config change request. Payload: { studentId }. Refetch the current config with GET /rostering/1.0/students/{studentId}/config. A subscription receives it when its org scope covers the student's primary organization or any organization on the student's roles. Non-breaking.
  • Applications OneRoster error envelope: imsx_codeMinorFieldValue now includes bad_gateway and gateway_timeout. Non-breaking.
  • Org config (GET and PUT /rostering/1.0/orgs/{sourcedId}/config): signalsActionRules replaces signalsIncidentRules. Each rule field actionVersion replaces incidentVersion. Stored values are carried over. The previous key is gone. Breaking for callers that read or write the old key or field.

2026.09.28.2

  • Insights: GET /insights/1.0/users/{userId} accepts optional sessionId, a 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. See Insights. Non-breaking.
  • Insights: GET /insights/1.0/users/{userId} lists a student's insights across sessions whose start time falls in after / before. after / before accept RFC 3339 timestamps with an offset (not only Z). limit accepts up to 1000 (default remains 20). See Insights. Non-breaking.

2026.09.28.1

  • Insights session summaries (GET /insights/1.0/users/{userId}/sessions, GET /insights/1.0/sessions/{sessionId}/insights, org session listings): subjectSource may now be enrollment, meaning the subject came from the student's app-scoped course enrolment on the session date. Non-breaking.
  • Insights org session listings (GET /insights/1.0/sessions), user session and insight reads (GET /insights/1.0/users/{userId}/sessions, GET /insights/1.0/users/{userId}, GET /insights/1.0/sessions/{sessionId}), and user overview (GET /insights/1.0/users/{userId}/overview) return sessions by the school stamped on each session. Overview accepts optional orgId and includeChildOrganizations. Unstamped sessions follow the prior live-enrollment rule. Breaking for callers that relied on a student's current school to surface another school's stamped sessions.

2026.09.27.1

  • Session enforcement state (GET /insights/1.0/sessions/{sessionId}/enforcement-state): on lockout, triggeringSessionId is null for lockouts written from this release on; the field was already nullable and its type is unchanged. Key on triggeringCaliperSessionId, which is now set whenever the triggering session has been ingested, including before any insight was raised on it. Non-breaking.

2026.09.24.2

  • Insights session lists (GET /insights/1.0/sessions and GET /insights/1.0/users/{userId}/sessions) and nested session summaries on session insight reads now include a tests array on each Alpha Test session. Each element carries attemptId, assessmentId, and testName so consumers can group multi-session tests by attemptId. Non-Alpha-Test sessions return tests: []. Non-breaking.

2026.09.24.1

  • Insights session summaries (GET /insights/1.0/users/{userId}/sessions, GET /insights/1.0/sessions/{sessionId}/insights, org session listings): subjectSource may now be xp, meaning the subject came from the first XP the student earned in that session. Non-breaking.

2026.09.23.1

  • List applications (GET /applications/1.0): subjects on each application is now the canonical CASE subject titles declared for that app, de-duplicated. Tokens that previously differed only by spelling or case (for example math and Mathematics) read back as one title. filter and sort on subjects use those same titles. Non-breaking.
  • XP day-summary (GET /xp/1.0/users/{userId}/day-summary): when a ledger row has no subject and no curriculum-item subject, the single-subject application fallback uses the application's CASE subject titles, not the retired text column. An app whose CASE join is empty stays in unassigned even if the frozen text column still has a token. Non-breaking.
  • Student config parent approval (POST /rostering/1.0/students/{sourcedId}/config/change-requests, GET /rostering/1.0/students/{sourcedId}/config/change-requests/latest, GET /rostering/1.0/config-change-requests): a student's primary organization with at-home camera and mic locked and atHomeCameraMicFlowMode set to email_approval can queue a parent-approval request from the student app when an open parent or guardian with a notifiable email is linked to the student; the school can read the latest request per student and list requests across its students. Create returns 409 when that primary org is missing or not on email approval. Non-breaking.
  • Org config (PUT /rostering/1.0/orgs/{sourcedId}/config): organizations can set atHomeCameraMicFlowMode to api or email_approval to choose whether at-home camera and mic changes go through the school portal or parent email approval when the lock is on. Non-breaking.

2026.09.22.2

  • Session enforcement state (GET /insights/1.0/sessions/{sessionId}/enforcement-state): currentSignalIncident is no longer returned. Signals-pipeline incident delivery through this endpoint has been reverted. Breaking for any consumer reading that field.
  • XP: GET /xp/1.0/users/{userId}/day-summary returns net XP for one org-local calendar day grouped by canonical CASE subject, with an explicit unassigned bucket and a totalXp that reconciles to the ledger for the same event-time window. Non-breaking.
  • XP: GET /xp/1.0/users/{userId}/entries treats the before query parameter as an exclusive upper bound on source event time, so after/before can match a day-summary window without double-counting the instant at window.end. Breaking for callers that relied on inclusive before at the boundary instant.

2026.09.22.1

  • Org config (PUT /rostering/1.0/orgs/{sourcedId}/config): organizations can set signalsSanctionedDestinations, a list of host or app labels treated as allowed leave-test destinations so matching signals insights are excluded from incident evaluation. Non-breaking.
  • Platform MCP (/mcp) and Developer-Platform MCP (/mcps/developer-platform): OAuth protected-resource metadata and the published authorization-server metadata now advertise client_credentials for machine clients. Both paths share the readOnly scope https://timeback-platform.trilogy.com/mcp/scope/readOnly. Google SSO on those paths is unchanged. The access token is threaded into tool context so webhook tools can authorize with the machine client's tenant grants. Insights and mastery reads stay requester-scoped: a machine token is rejected rather than falling through to unrestricted org access. Non-breaking.
  • Signals insights list (GET /signals/1.0/sessions/{sessionId}/insights): callers pass sessionId as a path parameter (replacing GET /signals/1.0/insights?sessionId=…); studentId, from, and to query filters are removed. Each insight row returns only pipeline fields (id, type, version, rule, ruleVersion, startedAt, endedAt); dateCreated, studentId, and sessionIds are removed. Breaking for integrators on the prior URL, query filters, or response fields.
  • Signals insight feedback submit (PUT /signals/1.0/insights/{insightId}/feedback): required OAuth scope is now https://timeback-platform.trilogy.com/signals/scope/feedback.write (was insights.write). Optional note is capped at 5000 characters. Breaking for clients that request insights.write or send longer notes.
  • Caliper ingest (POST /caliper/v1p2): events that omit session are attributed like urn:tag:auto-attach when the payload includes a Person actor and edApp, so they attach to a running session for that student and app instead of being stored with no session link. Non-breaking.

2026.09.21.2

  • Caliper ingest (POST /caliper/v1p2): each event's actor must resolve to a OneRoster user (urn:uuid only) unless the event is GradeEvent / Graded or SessionEvent / TimedOut, and each present edApp must resolve to a registered LTI application; otherwise the envelope is rejected with HTTP 400 before queueing. The CALIPER_EDAPP_REGISTRY_VALIDATION_ENABLED environment toggle is removed — edApp validation is always on when edApp is set. Breaking.

2026.09.21.1

  • Signals insights (GET /signals/1.0/insights, GET/PUT /signals/1.0/insights/{insightId}/feedback): school credentials and human callers can list pipeline insights in org or self scope, submit positive/negative feedback as one row per insight and submitter, and read feedback rows whose submitters are in scope. Non-breaking.
  • Session enforcement state (GET /insights/1.0/sessions/{sessionId}/enforcement-state): contract version 2 platform-authority responses may include currentSignalIncident, the oldest unacknowledged signals-pipeline incident for the session student when no live insights presentation is shown and the session is not in lockout. Non-breaking.

2026.09.20.1

  • Org config (PUT /rostering/1.0/orgs/{sourcedId}/config): organizations can set signalsIncidentRules, an array of rules that name which insight types open a signals incident, the count and rolling window, the student-facing message, and the incident version. Non-breaking.

2026.09.18.1

  • Analytics database schema guide: the published view catalog and column descriptions are now generated from analytics view entity metadata rather than live Postgres catalog comments. The overview describes the analytics MCP catalog instead of QuickSight. Data Type labels may differ from the previous Postgres-derived values. Non-breaking.
  • External Graders guide: the grade-time proxy section documents the consumer-backend caller with content.read, lists HTTP 401 for missing or invalid tokens, and drops the Coming Later note that grade auth was still pending. See External Graders. Non-breaking.

2026.09.17.1

  • Grading: POST /content/1.0/grade now requires a consumer-backend token with https://timeback-platform.trilogy.com/content/scope/content.read. Unauthenticated calls are rejected. The request and response body is unchanged. The platform still proxies a bound item to its registered grader and still sends that grader no student identity. See External Graders. Breaking for any caller that posted without a token.
  • Analytics guides: QuickSight dashboards, account invites, and scheduled snapshot export are no longer documented. Partners query the same curated analytics views through the Analytics MCP. Breaking for integrators who followed the retired QuickSight Setup guide.

2026.09.16.1

  • Webhooks: insight.created now fires only when an insight is auto-confirmed at creation. insight.deleted is no longer emitted. The insight.deleted event type stays registered so existing subscriptions can still be updated. Breaking for consumers that treated a later review confirmation as a create or a review reversal as a delete.
  • Grading: POST /content/1.0/grade now runs a platform-owned grader-hacking check on the student answer before forwarding a bound item to an external grader. A detected injection is a real grade of 0 with an empty comment and gradingProgress: FullyGraded; the grader is never called. If the check itself is down, the endpoint returns HTTP 502 with no score so the renderer can retry. See External Graders. Non-breaking.

2026.09.15.3

  • API Reference: published component names are now independent of unpublished modules. Collision suffixes such as InternalError22 and imsxCodeMinorFieldDType11 are gone; names that still collide among published operations are prefixed with the owning module (InsightsBadRequest, CaseNotFound). Breaking for anyone who generated a client against the previous component names.
  • Rostering classes and courses (PUT/DELETE /rostering/1.0/classes/{sourcedId} and /rostering/1.0/courses/{sourcedId}) and student config (GET/PUT /rostering/1.0/students/{sourcedId}/config): the 403 envelope is now the users module's own imsxStatusInfoDType (additionalProperties: false, with the same required fields and enums as the 400/404/500 on those operations). It is no longer the events module's Forbidden schema that the merge previously substituted. Non-breaking.
  • Caliper ingest (POST /caliper/v1p2) and session heartbeat (POST /events/1.0/sessions/{sessionId}/heartbeat): the 500 envelope is now the events module's own imsxStatusInfoDType rather than the users module's identically named schema the merge previously substituted. Same required fields and enums; descriptions are shorter and additionalProperties is unspecified. Non-breaking.
  • Insights guide: gate 2 of the customer-read gates no longer names insights_config.is_visible as the hide control. That boolean was retired; a finding's rollout stage is frozen at detection from the policy pinned on its session, so hide versus publish is the rollout stage and org policy alone. The guide previously said a hidden config stamped the finding SHADOW. Non-breaking.
  • Insights guide: gate 3 now names the per-type minimum occurrence duration, and findings a later event withdraws, alongside context-predicate misses as reasons a segment is stored as suppressed and kept off the Insights API, insight.* webhooks, and coaching analytics views. The guide previously gave a context-predicate miss as the only reason. Non-breaking.
  • Caliper ingest: a SessionEvent.Archived carrying SessionRecording.subject = Unknown no longer overwrites a subject the platform already classified. Unknown is treated as no information, so a producer that has no classification yet can send it safely. A named subject still updates the session's subject. See Caliper Events Reference. Non-breaking.

2026.09.15.2

  • Schools can read and set a student's at-home camera and microphone preference at GET/PUT /rostering/1.0/students/{sourcedId}/config (atHomeCameraMicEnabled). When the student has no stored override, the school's org default for that key is used; when the school has not set a default, the platform default is off. Org config also gains atHomeCameraMicLocked (off by default), which makes the in-app toggle read-only without rewriting stored student values. School-authenticated writes succeed whether or not the lock is on. Non-breaking.

2026.09.15.1

  • Grading: POST /content/1.0/grade now proxies a bound item to its registered external grader and returns that grader's scoreGiven and comment in the existing flat response. Timeout is 20 seconds (HTTP 504). An unusable result, unreachable grader, or a binding to a revoked grader is HTTP 502 with the reason and no score. Items with no binding still take the internal path. See External Graders. Non-breaking.
  • XP: MobyMax Language hole-filling pays both the lesson (5) and its Lesson Test: sibling (2) in the same UTC day. A student who completes both earns 7. This supersedes the 2026.09.12.1 rule that credited at most one of the pair. Historical declined_lesson_test_sibling scoring decisions remain on the record and are not restated. Non-breaking.
  • Insights catalog: AcademicIntegrityNoPaste (Pasted Into The Test) is registered in the OnlyTheTest group at SHADOW. Student messages and strikes stay off until rollout leaves SHADOW. Non-breaking.
  • Content ingestion: a published item may declare externalGraderId in extended metadata. The platform stores that as a nullable FK on the content item, or fails ingest if the grader is missing or revoked. Omitting the attribute leaves the default internal grade. See Content Ingestion. Non-breaking.
  • Analytics MCP: timeback_execute_query now has a 110-second statement timeout (was 25 seconds). The 500-row cap is unchanged. See Choosing an Integration Surface and Analytics. Non-breaking.

2026.09.12.1

  • XP: MobyMax Language hole-filling now credits at most one of the lesson (5) and its Lesson Test: sibling (2) per base skill per UTC day. When the assignment already lists the lesson test, the lesson completion is declined (declined_lesson_test_sibling) and only the passing lesson test pays 2. A missing stamp still pays the lesson 5. Non-breaking.

2026.09.10.1

  • Insights: POST /insights/1.0/sessions/{sessionId}/shares lets an organization-granted client_credentials app mint a session share and receive a Vault /share/{id} URL plus a seven-day expiry, so a partner can store that URL and deliver it on their own channel. Requires the same events.readonly scope as session insights. Non-breaking.

2026.09.09.1

  • Caliper ingest accepts SessionEvent with the Archived action: a Person actor delivers an inline SessionRecording object (proctored status, webcam usage, recording times, storage key, subject, extensions) for an existing session, and the platform applies the same session-metadata writes as POST /insights/1.0/sessions. The event never creates a session and never changes session start or end times. See Caliper Events Reference and Session Management. Non-breaking.

2026.09.08.2

  • Rostering users (GET /rostering/1.0/users, GET /rostering/1.0/users/{sourcedId}, and the role-scoped user lists): the agents array now contains only open agent relationships. Relationships the roster has ended (inactive, or carrying an end date) are excluded, matching the /users/{sourcedId}/agents and /users/{sourcedId}/linkedUsers endpoints. Breaking for integrators that read agents as the full relationship history.

2026.09.08.1

  • Webhooks: proctoring_enforcement.recorded now also fires for exited_mid_test (client-written when the student leaves the mid-test readiness screen without resuming). Filter on data.enforcementType. Analytics: proctoring_enforcement_actions.action_type includes that kind. Non-breaking.
  • Webhooks: proctoring_enforcement.recorded now also fires for blocked_mid_test (platform-minted when a CLIENT START fact carried a trusted READINESS_GATE mark) and unblocked_mid_test (client-written when the mid-test readiness dialog dismissed). Filter on data.enforcementType. Analytics: insight_events.enforcement_surface is READINESS_GATE when that mark was trusted, otherwise null. Non-breaking.
  • Store refunds: storefront purchases use an optional per-app refund window (default 7 days) set with set_app_refund_window; in-app refunds stay on the global 7-day window. Non-breaking.

2026.09.07.2

  • Breaking: IRI-only actor and edApp values that have no Caliper entity yet are defined from the matching OneRoster user or LTI application. If neither registry has a match, ingest rejects the event instead of creating a stub. Other entity references keep stub semantics. See General Concepts.

2026.09.07.1

  • Analytics schema: proctoring_enforcement_actions and insight_notifications withhold internal catalogue rule identities instead of dropping those rows. When the resolved type visibility is internal, insight_type and insight_group are null and the new rule_visibility column is internal. insight_notifications.insight_type is now nullable (zero current rows affected). Non-breaking for callers that already treated those identity columns as optional on enforcement actions.

2026.09.03.1

  • Store Discount Codes guide: documents POST /apps/v1/discount-codes/validate (Cognito client-credentials, same purchases.write scope as in-app purchases). Parent checkout remains POST /store/v1/discount-codes/validate with a parent session. Non-breaking.
  • Insights guide: insightGroup is required when the resulting type is not Journal. A Journal type may omit it (insightGroup: null). An update whose merged state is a non-Journal type without a group is refused. Previously the guide said a group was required on every new type. Non-breaking.

2026.09.01.1

  • New insight findings are no longer hidden by insights_config.is_visible or org insightTypeConfigOverrides.isVisible. Hide vs publish is rollout_state and org policy only. The override JSON key is still accepted and ignored. Non-breaking for readers; operators who hid a detector only via that boolean should use a SHADOW org policy.

2026.08.31.1

  • Insights guide and webhooks: new findings default to SHADOW when the session has no pinned rollout stage for that type. Remaining rolloutState nulls will be filled to SHADOW; until that fill, a null stage still passes the customer-read gate. New unpinned detections stay off the customer read API and insight.* events.

2026.08.28.2

  • Insight types can carry a minimum occurrence duration. Occurrences shorter than that minimum are stored as suppressed rows and are absent from the Insights API, webhooks, and coaching analytics views. Types with no minimum configured are unchanged. Non-breaking.

2026.08.27.2

  • Insights types IgnoringHelp and TopicShopping stop producing new detections in this release. GET /insights/1.0/types keeps both in the catalog with deprecated: true and sunsetAt: 2026-11-26T00:00:00.000Z; after that datetime they leave the catalog. Insights already recorded against either type remain readable. Non-breaking.

2026.08.27.1

  • Insights guide: customer reads of detections now also require insights_insight.outcome = raised. Segments whose context predicate failed (for example an academic-integrity signal on a standard session, or minutes outside an assessment window) are stored as suppressed rows and are absent from the Insights API, webhooks, and coaching analytics views. Non-breaking for callers that already consumed only raised detections; suppressed minutes no longer inflate waste or integrity totals.

2026.08.26.1

  • Insights API Reference: GET /insights/1.0/types now returns deprecated (boolean, always present) and sunsetAt (nullable RFC 3339 datetime) on each type. During a deprecation window the type stays in the catalog; after sunsetAt it leaves the catalog and stops producing new insights. Previously recorded insights remain readable. Non-breaking.

2026.08.24.1

  • Webhooks guide: org.config.updated is now in the event catalog. It fires when an organization's config row set is written through PUT /rostering/1.0/orgs/{sourcedId}/config (any key write). Payload: { orgId }. Subscription routing follows the standard org-scope rule. Non-breaking.

2026.08.17.1

  • Insights API Reference: GET /insights/1.0/insight-groups is now published. It returns the registered insight groups with family, membership rule, and a count of types visible to the caller in each group. Non-breaking.

2026.08.11.1

  • Insights API Reference: GET /insights/1.0/catalog has been removed from the published surface. It returned locale-keyed message bundles for catalog-managed proctoring insights and was consumed at session start. Breaking for integrators that called it directly.

2026.08.03.1

  • The insight types the guide used to list are marked deprecated: they are being retired and will stop being populated. Pull the current list from the type catalog. Breaking for integrators that hard-coded those slugs.